Blender V5.0
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
10
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_ordered_edge.hh"
29#include "BLI_polyfill_2d.h"
30#include "BLI_string.h"
31#include "BLI_string_utf8.h"
32#include "BLI_task.hh"
33#include "BLI_utildefines.h"
34
35#include "BKE_attribute.hh"
36#include "BKE_customdata.hh"
37#include "BKE_global.hh"
38#include "BKE_idprop.hh"
39#include "BKE_lib_id.hh"
40#include "BKE_main.hh"
41#include "BKE_main_namemap.hh"
42#include "BKE_mesh.hh"
44#include "BKE_modifier.hh"
45#include "BKE_multires.hh"
46#include "BKE_node.hh"
47#include "BKE_node_runtime.hh"
49
50#include "BLT_translation.hh"
51
53using blender::Span;
54
55/* -------------------------------------------------------------------- */
58
59struct EdgeSort {
62};
63
64/* edges have to be added with lowest index first for sorting */
65static void to_edgesort(EdgeSort *ed, uint v1, uint v2, char is_loose, short is_draw)
66{
67 if (v1 < v2) {
68 ed->v1 = v1;
69 ed->v2 = v2;
70 }
71 else {
72 ed->v1 = v2;
73 ed->v2 = v1;
74 }
75 ed->is_loose = is_loose;
76 ed->is_draw = is_draw;
77}
78
79static int vergedgesort(const void *v1, const void *v2)
80{
81 const EdgeSort *x1 = static_cast<const EdgeSort *>(v1);
82 const EdgeSort *x2 = static_cast<const EdgeSort *>(v2);
83
84 if (x1->v1 > x2->v1) {
85 return 1;
86 }
87 if (x1->v1 < x2->v1) {
88 return -1;
89 }
90 if (x1->v2 > x2->v2) {
91 return 1;
92 }
93 if (x1->v2 < x2->v2) {
94 return -1;
95 }
96
97 return 0;
98}
99
100/* Create edges based on known verts and faces,
101 * this function is only used when loading very old blend files */
102static void mesh_calc_edges_mdata(const MVert * /*allvert*/,
103 const MFace *allface,
104 MLoop *allloop,
105 const MPoly *allpoly,
106 int /*totvert*/,
107 int totface,
108 int /*totloop*/,
109 int faces_num,
110 MEdge **r_medge,
111 int *r_totedge)
112{
113 const MPoly *mpoly;
114 const MFace *mface;
115 MEdge *edges, *edge;
116 EdgeSort *edsort, *ed;
117 int a, totedge = 0;
118 uint totedge_final = 0;
119 uint edge_index;
120
121 /* we put all edges in array, sort them, and detect doubles that way */
122
123 for (a = totface, mface = allface; a > 0; a--, mface++) {
124 if (mface->v4) {
125 totedge += 4;
126 }
127 else if (mface->v3) {
128 totedge += 3;
129 }
130 else {
131 totedge += 1;
132 }
133 }
134
135 if (totedge == 0) {
136 *r_medge = nullptr;
137 *r_totedge = 0;
138 return;
139 }
140
141 ed = edsort = MEM_malloc_arrayN<EdgeSort>(totedge, "EdgeSort");
142
143 for (a = totface, mface = allface; a > 0; a--, mface++) {
144 to_edgesort(ed++, mface->v1, mface->v2, !mface->v3, mface->edcode & ME_V1V2);
145 if (mface->v4) {
146 to_edgesort(ed++, mface->v2, mface->v3, 0, mface->edcode & ME_V2V3);
147 to_edgesort(ed++, mface->v3, mface->v4, 0, mface->edcode & ME_V3V4);
148 to_edgesort(ed++, mface->v4, mface->v1, 0, mface->edcode & ME_V4V1);
149 }
150 else if (mface->v3) {
151 to_edgesort(ed++, mface->v2, mface->v3, 0, mface->edcode & ME_V2V3);
152 to_edgesort(ed++, mface->v3, mface->v1, 0, mface->edcode & ME_V3V1);
153 }
154 }
155
156 qsort(edsort, totedge, sizeof(EdgeSort), vergedgesort);
157
158 /* count final amount */
159 for (a = totedge, ed = edsort; a > 1; a--, ed++) {
160 /* edge is unique when it differs from next edge, or is last */
161 if (ed->v1 != (ed + 1)->v1 || ed->v2 != (ed + 1)->v2) {
162 totedge_final++;
163 }
164 }
165 totedge_final++;
166
167 edges = MEM_calloc_arrayN<MEdge>(totedge_final, __func__);
168
169 for (a = totedge, edge = edges, ed = edsort; a > 1; a--, ed++) {
170 /* edge is unique when it differs from next edge, or is last */
171 if (ed->v1 != (ed + 1)->v1 || ed->v2 != (ed + 1)->v2) {
172 edge->v1 = ed->v1;
173 edge->v2 = ed->v2;
174
175 /* order is swapped so extruding this edge as a surface won't flip face normals
176 * with cyclic curves */
177 if (ed->v1 + 1 != ed->v2) {
178 std::swap(edge->v1, edge->v2);
179 }
180 edge++;
181 }
182 else {
183 /* Equal edge, merge the draw-flag. */
184 (ed + 1)->is_draw |= ed->is_draw;
185 }
186 }
187 /* last edge */
188 edge->v1 = ed->v1;
189 edge->v2 = ed->v2;
190
191 MEM_freeN(edsort);
192
193 /* set edge members of mloops */
195 hash.reserve(totedge_final);
196 for (edge_index = 0, edge = edges; edge_index < totedge_final; edge_index++, edge++) {
197 hash.add({edge->v1, edge->v2}, edge_index);
198 }
199
200 mpoly = allpoly;
201 for (a = 0; a < faces_num; a++, mpoly++) {
202 MLoop *ml, *ml_next;
203 int i = mpoly->totloop;
204
205 ml_next = allloop + mpoly->loopstart; /* first loop */
206 ml = &ml_next[i - 1]; /* last loop */
207
208 while (i-- != 0) {
209 ml->e = hash.lookup({ml->v, ml_next->v});
210 ml = ml_next;
211 ml_next++;
212 }
213 }
214
215 BLI_assert(totedge_final > 0);
216 *r_medge = edges;
217 *r_totedge = totedge_final;
218}
219
221{
222 using namespace blender;
223 MEdge *edges;
224 int totedge = 0;
225 const Span<MVert> verts(
226 static_cast<const MVert *>(CustomData_get_layer(&mesh->vert_data, CD_MVERT)),
227 mesh->verts_num);
228
230 verts.data(),
231 mesh->mface,
232 static_cast<MLoop *>(
234 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
235 verts.size(),
236 mesh->totface_legacy,
237 mesh->corners_num,
238 mesh->faces_num,
239 &edges,
240 &totedge);
241
242 if (totedge == 0) {
243 BLI_assert(edges == nullptr);
244 mesh->edges_num = 0;
245 return;
246 }
247
248 edges = (MEdge *)CustomData_add_layer_with_data(
249 &mesh->edge_data, CD_MEDGE, edges, totedge, nullptr);
250 mesh->edges_num = totedge;
251
252 mesh->tag_topology_changed();
254}
255
257{
258 /* NOTE: We need to keep this for edge creation (for now?), and some old `readfile.cc` code. */
259 MFace *f;
260 int a, b;
261 MFace *mfaces = mesh->mface;
262
263 for (a = b = 0, f = mfaces; a < mesh->totface_legacy; a++, f++) {
264 if (f->v3) {
265 if (a != b) {
266 memcpy(&mfaces[b], f, sizeof(mfaces[b]));
267 CustomData_copy_data(&mesh->fdata_legacy, &mesh->fdata_legacy, a, b, 1);
268 }
269 b++;
270 }
271 }
272 if (a != b) {
273 CustomData_free_elem(&mesh->fdata_legacy, b, a - b);
274 mesh->totface_legacy = b;
275 }
276}
277
279
280/* -------------------------------------------------------------------- */
283
285{
286 using namespace blender;
287 if (UNLIKELY(mesh->cd_flag)) {
288 return;
289 }
290
291 const Span<MVert> verts(
292 static_cast<const MVert *>(CustomData_get_layer(&mesh->vert_data, CD_MVERT)),
293 mesh->verts_num);
294 const Span<MEdge> edges(
295 static_cast<const MEdge *>(CustomData_get_layer(&mesh->edge_data, CD_MEDGE)),
296 mesh->edges_num);
297
298 for (const MVert &vert : verts) {
299 if (vert.bweight_legacy != 0) {
300 mesh->cd_flag |= ME_CDFLAG_VERT_BWEIGHT;
301 break;
302 }
303 }
304
305 for (const MEdge &edge : edges) {
306 if (edge.bweight_legacy != 0) {
307 mesh->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
308 if (mesh->cd_flag & ME_CDFLAG_EDGE_CREASE) {
309 break;
310 }
311 }
312 if (edge.crease_legacy != 0) {
313 mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
314 if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
315 break;
316 }
317 }
318 }
319}
320
322
323/* -------------------------------------------------------------------- */
326
327#define MESH_MLOOPCOL_FROM_MCOL(_mloopcol, _mcol) \
328 { \
329 MLoopCol *mloopcol__tmp = _mloopcol; \
330 const MCol *mcol__tmp = _mcol; \
331 mloopcol__tmp->r = mcol__tmp->b; \
332 mloopcol__tmp->g = mcol__tmp->g; \
333 mloopcol__tmp->b = mcol__tmp->r; \
334 mloopcol__tmp->a = mcol__tmp->a; \
335 } \
336 (void)0
337
339 CustomData *fdata_legacy,
340 const int totface,
341 CustomData *ldata,
342 MFace *mface,
343 int totloop,
344 int findex,
345 int loopstart,
346 int numTex,
347 int numCol)
348{
349 MFace *mf = mface + findex;
350
351 for (int i = 0; i < numTex; i++) {
352 const MTFace *texface = (const MTFace *)CustomData_get_n_for_write(
353 fdata_legacy, CD_MTFACE, findex, i, totface);
354
355 blender::float2 *uv = static_cast<blender::float2 *>(
356 CustomData_get_n_for_write(ldata, CD_PROP_FLOAT2, loopstart, i, totloop));
357 copy_v2_v2(*uv, texface->uv[0]);
358 uv++;
359 copy_v2_v2(*uv, texface->uv[1]);
360 uv++;
361 copy_v2_v2(*uv, texface->uv[2]);
362 uv++;
363
364 if (mf->v4) {
365 copy_v2_v2(*uv, texface->uv[3]);
366 uv++;
367 }
368 }
369
370 for (int i = 0; i < numCol; i++) {
372 ldata, CD_PROP_BYTE_COLOR, loopstart, i, totloop);
373 const MCol *mcol = (const MCol *)CustomData_get_n_for_write(
374 fdata_legacy, CD_MCOL, findex, i, totface);
375
376 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[0]);
377 mloopcol++;
378 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[1]);
379 mloopcol++;
380 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[2]);
381 mloopcol++;
382 if (mf->v4) {
383 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[3]);
384 mloopcol++;
385 }
386 }
387
388 if (CustomData_has_layer(fdata_legacy, CD_TESSLOOPNORMAL)) {
389 float (*loop_normals)[3] = (float (*)[3])CustomData_get_for_write(
390 ldata, loopstart, CD_NORMAL, totloop);
391 const short (*tessloop_normals)[3] = (short (*)[3])CustomData_get_for_write(
392 fdata_legacy, findex, CD_TESSLOOPNORMAL, totface);
393 const int max = mf->v4 ? 4 : 3;
394
395 for (int i = 0; i < max; i++, loop_normals++, tessloop_normals++) {
396 normal_short_to_float_v3(*loop_normals, *tessloop_normals);
397 }
398 }
399
400 if (CustomData_has_layer(fdata_legacy, CD_MDISPS)) {
401 MDisps *ld = (MDisps *)CustomData_get_for_write(ldata, loopstart, CD_MDISPS, totloop);
402 const MDisps *fd = (const MDisps *)CustomData_get_for_write(
403 fdata_legacy, findex, CD_MDISPS, totface);
404 const float (*disps)[3] = fd->disps;
405 int tot = mf->v4 ? 4 : 3;
406 int corners;
407
408 if (CustomData_external_test(fdata_legacy, CD_MDISPS)) {
409 if (id && fdata_legacy->external) {
410 CustomData_external_add(ldata, id, CD_MDISPS, totloop, fdata_legacy->external->filepath);
411 }
412 }
413
414 corners = multires_mdisp_corners(fd);
415
416 if (corners == 0) {
417 /* Empty #MDisp layers appear in at least one of the `sintel.blend` files.
418 * Not sure why this happens, but it seems fine to just ignore them here.
419 * If `corners == 0` for a non-empty layer though, something went wrong. */
420 BLI_assert(fd->totdisp == 0);
421 }
422 else {
423 const int side = int(sqrtf(float(fd->totdisp / corners)));
424 const int side_sq = side * side;
425
426 for (int i = 0; i < tot; i++, disps += side_sq, ld++) {
427 ld->totdisp = side_sq;
428 ld->level = int(logf(float(side) - 1.0f) / float(M_LN2)) + 1;
429
430 if (ld->disps) {
431 MEM_freeN(ld->disps);
432 }
433
434 ld->disps = MEM_malloc_arrayN<float[3]>(size_t(side_sq), "converted loop mdisps");
435 if (fd->disps) {
436 memcpy(ld->disps, disps, size_t(side_sq) * sizeof(float[3]));
437 }
438 else {
439 memset(ld->disps, 0, size_t(side_sq) * sizeof(float[3]));
440 }
441 }
442 }
443 }
444}
445
446static void CustomData_to_bmeshpoly(CustomData *fdata_legacy, CustomData *ldata, int totloop)
447{
448 for (int i = 0; i < fdata_legacy->totlayer; i++) {
449 if (fdata_legacy->layers[i].type == CD_MTFACE) {
451 ldata, CD_PROP_FLOAT2, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
452 }
453 else if (fdata_legacy->layers[i].type == CD_MCOL) {
455 ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
456 }
457 else if (fdata_legacy->layers[i].type == CD_MDISPS) {
459 ldata, CD_MDISPS, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
460 }
461 else if (fdata_legacy->layers[i].type == CD_TESSLOOPNORMAL) {
463 ldata, CD_NORMAL, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
464 }
465 }
466}
467
469 CustomData *fdata_legacy,
470 CustomData *ldata,
471 CustomData *pdata,
472 int totedge_i,
473 int totface_i,
474 int /*totloop_i*/,
475 int /*faces_num_i*/,
476 blender::int2 *edges,
477 MFace *mface,
478 int *r_totloop,
479 int *r_faces_num)
480{
481 MFace *mf;
482 MLoop *ml, *mloop;
483 MPoly *poly, *mpoly;
484 int numTex, numCol;
485 int i, j, totloop, faces_num, *polyindex;
486
487 /* just in case some of these layers are filled in (can happen with python created meshes) */
488 CustomData_free(ldata);
489 CustomData_free(pdata);
490
491 faces_num = totface_i;
492 mpoly = (MPoly *)CustomData_add_layer(pdata, CD_MPOLY, CD_SET_DEFAULT, faces_num);
493 int *material_indices = static_cast<int *>(
494 CustomData_get_layer_named_for_write(pdata, CD_PROP_INT32, "material_index", faces_num));
495 if (material_indices == nullptr) {
496 material_indices = static_cast<int *>(CustomData_add_layer_named(
497 pdata, CD_PROP_INT32, CD_SET_DEFAULT, faces_num, "material_index"));
498 }
499 bool *sharp_faces = static_cast<bool *>(
500 CustomData_get_layer_named_for_write(pdata, CD_PROP_BOOL, "sharp_face", faces_num));
501 if (!sharp_faces) {
502 sharp_faces = static_cast<bool *>(
503 CustomData_add_layer_named(pdata, CD_PROP_BOOL, CD_SET_DEFAULT, faces_num, "sharp_face"));
504 }
505
506 numTex = CustomData_number_of_layers(fdata_legacy, CD_MTFACE);
507 numCol = CustomData_number_of_layers(fdata_legacy, CD_MCOL);
508
509 totloop = 0;
510 mf = mface;
511 for (i = 0; i < totface_i; i++, mf++) {
512 totloop += mf->v4 ? 4 : 3;
513 }
514
515 mloop = (MLoop *)CustomData_add_layer(ldata, CD_MLOOP, CD_SET_DEFAULT, totloop);
516
517 CustomData_to_bmeshpoly(fdata_legacy, ldata, totloop);
518
519 if (id) {
520 /* ensure external data is transferred */
521 /* TODO(sergey): Use multiresModifier_ensure_external_read(). */
522 CustomData_external_read(fdata_legacy, id, CD_MASK_MDISPS, totface_i);
523 }
524
526 eh.reserve(totedge_i);
527
528 /* build edge hash */
529 for (i = 0; i < totedge_i; i++) {
530 eh.add(edges[i], i);
531 }
532
533 polyindex = (int *)CustomData_get_layer(fdata_legacy, CD_ORIGINDEX);
534
535 j = 0; /* current loop index */
536 ml = mloop;
537 mf = mface;
538 poly = mpoly;
539 for (i = 0; i < totface_i; i++, mf++, poly++) {
540 poly->loopstart = j;
541
542 poly->totloop = mf->v4 ? 4 : 3;
543
544 material_indices[i] = mf->mat_nr;
545 sharp_faces[i] = (mf->flag & ME_SMOOTH) == 0;
546
547#define ML(v1, v2) \
548 { \
549 ml->v = mf->v1; \
550 ml->e = eh.lookup({mf->v1, mf->v2}); \
551 ml++; \
552 j++; \
553 } \
554 (void)0
555
556 ML(v1, v2);
557 ML(v2, v3);
558 if (mf->v4) {
559 ML(v3, v4);
560 ML(v4, v1);
561 }
562 else {
563 ML(v3, v1);
564 }
565
566#undef ML
567
569 id, fdata_legacy, totface_i, ldata, mface, totloop, i, poly->loopstart, numTex, numCol);
570
571 if (polyindex) {
572 *polyindex = i;
573 polyindex++;
574 }
575 }
576
577 /* NOTE: we don't convert NGons at all, these are not even real ngons,
578 * they have their own UVs, colors etc - it's more an editing feature. */
579
580 *r_faces_num = faces_num;
581 *r_totloop = totloop;
582}
583
584static void update_active_fdata_layers(Mesh &mesh, CustomData *fdata_legacy, CustomData *ldata)
585{
586 int act;
587
590 CustomData_set_layer_active(fdata_legacy, CD_MTFACE, act);
591
593 CustomData_set_layer_render(fdata_legacy, CD_MTFACE, act);
594
596 CustomData_set_layer_clone(fdata_legacy, CD_MTFACE, act);
597
599 CustomData_set_layer_stencil(fdata_legacy, CD_MTFACE, act);
600 }
601
603 if (mesh.active_color_attribute != nullptr) {
605 /* The active color layer may be of #CD_PROP_COLOR type. */
606 if (act != -1) {
607 CustomData_set_layer_active(fdata_legacy, CD_MCOL, act);
608 }
609 }
610
611 if (mesh.default_color_attribute != nullptr) {
613 /* The active color layer may be of #CD_PROP_COLOR type. */
614 if (act != -1) {
615 CustomData_set_layer_render(fdata_legacy, CD_MCOL, act);
616 }
617 }
618
620 CustomData_set_layer_clone(fdata_legacy, CD_MCOL, act);
621
623 CustomData_set_layer_stencil(fdata_legacy, CD_MCOL, act);
624 }
625}
626
627#ifndef NDEBUG
635 CustomData *ldata,
636 bool fallback)
637{
638 int a_num = 0, b_num = 0;
639# define LAYER_CMP(l_a, t_a, l_b, t_b) \
640 ((a_num += CustomData_number_of_layers(l_a, t_a)) == \
641 (b_num += CustomData_number_of_layers(l_b, t_b)))
642
643 if (!LAYER_CMP(ldata, CD_PROP_FLOAT2, fdata_legacy, CD_MTFACE)) {
644 return false;
645 }
646 if (!LAYER_CMP(ldata, CD_PROP_BYTE_COLOR, fdata_legacy, CD_MCOL)) {
647 return false;
648 }
649 if (!LAYER_CMP(ldata, CD_ORIGSPACE_MLOOP, fdata_legacy, CD_ORIGSPACE)) {
650 return false;
651 }
652 if (!LAYER_CMP(ldata, CD_NORMAL, fdata_legacy, CD_TESSLOOPNORMAL)) {
653 return false;
654 }
655
656# undef LAYER_CMP
657
658 /* if no layers are on either CustomData's,
659 * then there was nothing to do... */
660 return a_num ? true : fallback;
661}
662#endif /* !NDEBUG */
663
664static void add_mface_layers(Mesh &mesh, CustomData *fdata_legacy, CustomData *ldata, int total)
665{
666 /* avoid accumulating extra layers */
667 BLI_assert(!check_matching_legacy_layer_counts(fdata_legacy, ldata, false));
668
669 for (int i = 0; i < ldata->totlayer; i++) {
670 if (ldata->layers[i].type == CD_PROP_FLOAT2) {
672 fdata_legacy, CD_MTFACE, CD_SET_DEFAULT, total, ldata->layers[i].name);
673 }
674 if (ldata->layers[i].type == CD_PROP_BYTE_COLOR) {
676 fdata_legacy, CD_MCOL, CD_SET_DEFAULT, total, ldata->layers[i].name);
677 }
678 else if (ldata->layers[i].type == CD_ORIGSPACE_MLOOP) {
680 fdata_legacy, CD_ORIGSPACE, CD_SET_DEFAULT, total, ldata->layers[i].name);
681 }
682 else if (ldata->layers[i].type == CD_NORMAL) {
684 fdata_legacy, CD_TESSLOOPNORMAL, 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{
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(),
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{
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(),
805 &mesh->corners_num,
806 &mesh->faces_num);
809
811
813}
814
816
817/* -------------------------------------------------------------------- */
822
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 int findex, i, j;
859 const int *pidx;
860 uint(*lidx)[4];
861
862 for (i = 0; i < numUV; i++) {
864 fdata_legacy, CD_MTFACE, i, num_faces);
865 const blender::float2 *uv = static_cast<const blender::float2 *>(
867
868 for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces;
869 pidx++, lidx++, findex++, texface++)
870 {
871 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
872 copy_v2_v2(texface->uv[j], uv[(*lidx)[j]]);
873 }
874 }
875 }
876
877 for (i = 0; i < numCol; i++) {
878 MCol(*mcol)[4] = (MCol(*)[4])CustomData_get_layer_n_for_write(
879 fdata_legacy, CD_MCOL, i, num_faces);
880 const MLoopCol *mloopcol = (const MLoopCol *)CustomData_get_layer_n(
881 corner_data, CD_PROP_BYTE_COLOR, i);
882
883 for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) {
884 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
885 MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]);
886 }
887 }
888 }
889
890 if (hasOrigSpace) {
892 const OrigSpaceLoop *lof = (const OrigSpaceLoop *)CustomData_get_layer(corner_data,
894
895 for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) {
896 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
897 copy_v2_v2(of->uv[j], lof[(*lidx)[j]].uv);
898 }
899 }
900 }
901
902 if (hasLoopNormal) {
903 short (*face_normals)[4][3] = (short (*)[4][3])CustomData_get_layer(fdata_legacy,
905 const float (*loop_normals)[3] = (const float (*)[3])CustomData_get_layer(corner_data,
906 CD_NORMAL);
907
908 for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, face_normals++) {
909 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
910 normal_float_to_short_v3((*face_normals)[j], loop_normals[(*lidx)[j]]);
911 }
912 }
913 }
914}
915
916int BKE_mesh_mface_index_validate(MFace *mface, CustomData *fdata_legacy, int mfindex, int nr)
917{
918 /* first test if the face is legal */
919 if ((mface->v3 || nr == 4) && mface->v3 == mface->v4) {
920 mface->v4 = 0;
921 nr--;
922 }
923 if ((mface->v2 || mface->v4) && mface->v2 == mface->v3) {
924 mface->v3 = mface->v4;
925 mface->v4 = 0;
926 nr--;
927 }
928 if (mface->v1 == mface->v2) {
929 mface->v2 = mface->v3;
930 mface->v3 = mface->v4;
931 mface->v4 = 0;
932 nr--;
933 }
934
935 /* Check corrupt cases, bow-tie geometry,
936 * can't handle these because edge data won't exist so just return 0. */
937 if (nr == 3) {
938 if (
939 /* real edges */
940 mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v1)
941 {
942 return 0;
943 }
944 }
945 else if (nr == 4) {
946 if (
947 /* real edges */
948 mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v4 ||
949 mface->v4 == mface->v1 ||
950 /* across the face */
951 mface->v1 == mface->v3 || mface->v2 == mface->v4)
952 {
953 return 0;
954 }
955 }
956
957 /* prevent a zero at wrong index location */
958 if (nr == 3) {
959 if (mface->v3 == 0) {
960 static int corner_indices[4] = {1, 2, 0, 3};
961
962 std::swap(mface->v1, mface->v2);
963 std::swap(mface->v2, mface->v3);
964
965 if (fdata_legacy) {
966 CustomData_swap_corners(fdata_legacy, mfindex, corner_indices);
967 }
968 }
969 }
970 else if (nr == 4) {
971 if (mface->v3 == 0 || mface->v4 == 0) {
972 static int corner_indices[4] = {2, 3, 0, 1};
973
974 std::swap(mface->v1, mface->v3);
975 std::swap(mface->v2, mface->v4);
976
977 if (fdata_legacy) {
978 CustomData_swap_corners(fdata_legacy, mfindex, corner_indices);
979 }
980 }
981 }
982
983 return nr;
984}
985
986static int mesh_tessface_calc(Mesh &mesh,
987 CustomData *fdata_legacy,
988 CustomData *ldata,
989 CustomData *pdata,
990 float (*positions)[3],
991 int totface,
992 int totloop,
993 int faces_num)
994{
995#define USE_TESSFACE_SPEEDUP
996#define USE_TESSFACE_QUADS
997
998/* We abuse #MFace.edcode to tag quad faces. See below for details. */
999#define TESSFACE_IS_QUAD 1
1000
1001 const int corner_tris_num = poly_to_tri_count(faces_num, totloop);
1002
1003 MFace *mface, *mf;
1004 MemArena *arena = nullptr;
1005 int *mface_to_poly_map;
1006 uint(*lindices)[4];
1007 int poly_index, mface_index;
1008 uint j;
1009
1010 const blender::OffsetIndices faces = mesh.faces();
1011 const Span<int> corner_verts = mesh.corner_verts();
1012 const int *material_indices = static_cast<const int *>(
1013 CustomData_get_layer_named(pdata, CD_PROP_INT32, "material_index"));
1014 const bool *sharp_faces = static_cast<const bool *>(
1015 CustomData_get_layer_named(pdata, CD_PROP_BOOL, "sharp_face"));
1016
1017 /* Allocate the length of `totfaces`, avoid many small reallocation's,
1018 * if all faces are triangles it will be correct, `quads == 2x` allocations. */
1019 /* Take care since memory is _not_ zeroed so be sure to initialize each field. */
1020 mface_to_poly_map = MEM_malloc_arrayN<int>(size_t(corner_tris_num), __func__);
1021 mface = MEM_malloc_arrayN<MFace>(size_t(corner_tris_num), __func__);
1022 lindices = MEM_malloc_arrayN<uint[4]>(size_t(corner_tris_num), __func__);
1023
1024 mface_index = 0;
1025 for (poly_index = 0; poly_index < faces_num; poly_index++) {
1026 const uint mp_loopstart = uint(faces[poly_index].start());
1027 const uint mp_totloop = uint(faces[poly_index].size());
1028 uint l1, l2, l3, l4;
1029 uint *lidx;
1030 if (mp_totloop < 3) {
1031 /* Do nothing. */
1032 }
1033
1034#ifdef USE_TESSFACE_SPEEDUP
1035
1036# define ML_TO_MF(i1, i2, i3) \
1037 mface_to_poly_map[mface_index] = poly_index; \
1038 mf = &mface[mface_index]; \
1039 lidx = lindices[mface_index]; \
1040 /* Set loop indices, transformed to vert indices later. */ \
1041 l1 = mp_loopstart + i1; \
1042 l2 = mp_loopstart + i2; \
1043 l3 = mp_loopstart + i3; \
1044 mf->v1 = corner_verts[l1]; \
1045 mf->v2 = corner_verts[l2]; \
1046 mf->v3 = corner_verts[l3]; \
1047 mf->v4 = 0; \
1048 lidx[0] = l1; \
1049 lidx[1] = l2; \
1050 lidx[2] = l3; \
1051 lidx[3] = 0; \
1052 mf->mat_nr = material_indices ? material_indices[poly_index] : 0; \
1053 mf->flag = (sharp_faces && sharp_faces[poly_index]) ? 0 : ME_SMOOTH; \
1054 mf->edcode = 0; \
1055 (void)0
1056
1057/* ALMOST IDENTICAL TO DEFINE ABOVE (see EXCEPTION) */
1058# define ML_TO_MF_QUAD() \
1059 mface_to_poly_map[mface_index] = poly_index; \
1060 mf = &mface[mface_index]; \
1061 lidx = lindices[mface_index]; \
1062 /* Set loop indices, transformed to vert indices later. */ \
1063 l1 = mp_loopstart + 0; /* EXCEPTION */ \
1064 l2 = mp_loopstart + 1; /* EXCEPTION */ \
1065 l3 = mp_loopstart + 2; /* EXCEPTION */ \
1066 l4 = mp_loopstart + 3; /* EXCEPTION */ \
1067 mf->v1 = corner_verts[l1]; \
1068 mf->v2 = corner_verts[l2]; \
1069 mf->v3 = corner_verts[l3]; \
1070 mf->v4 = corner_verts[l4]; \
1071 lidx[0] = l1; \
1072 lidx[1] = l2; \
1073 lidx[2] = l3; \
1074 lidx[3] = l4; \
1075 mf->mat_nr = material_indices ? material_indices[poly_index] : 0; \
1076 mf->flag = (sharp_faces && sharp_faces[poly_index]) ? 0 : ME_SMOOTH; \
1077 mf->edcode = TESSFACE_IS_QUAD; \
1078 (void)0
1079
1080 else if (mp_totloop == 3) {
1081 ML_TO_MF(0, 1, 2);
1082 mface_index++;
1083 }
1084 else if (mp_totloop == 4) {
1085# ifdef USE_TESSFACE_QUADS
1086 ML_TO_MF_QUAD();
1087 mface_index++;
1088# else
1089 ML_TO_MF(0, 1, 2);
1090 mface_index++;
1091 ML_TO_MF(0, 2, 3);
1092 mface_index++;
1093# endif
1094 }
1095#endif /* USE_TESSFACE_SPEEDUP */
1096 else {
1097 const float *co_curr, *co_prev;
1098
1099 float normal[3];
1100
1101 float axis_mat[3][3];
1102 float (*projverts)[2];
1103 uint(*tris)[3];
1104
1105 const uint totfilltri = mp_totloop - 2;
1106
1107 if (UNLIKELY(arena == nullptr)) {
1108 arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
1109 }
1110
1111 tris = (uint(*)[3])BLI_memarena_alloc(arena, sizeof(*tris) * size_t(totfilltri));
1112 projverts = (float (*)[2])BLI_memarena_alloc(arena, sizeof(*projverts) * size_t(mp_totloop));
1113
1114 zero_v3(normal);
1115
1116 /* Calculate the normal, flipped: to get a positive 2D cross product. */
1117 co_prev = positions[corner_verts[mp_loopstart + mp_totloop - 1]];
1118 for (j = 0; j < mp_totloop; j++) {
1119 const int vert = corner_verts[mp_loopstart + j];
1120 co_curr = positions[vert];
1121 add_newell_cross_v3_v3v3(normal, co_prev, co_curr);
1122 co_prev = co_curr;
1123 }
1124 if (UNLIKELY(normalize_v3(normal) == 0.0f)) {
1125 normal[2] = 1.0f;
1126 }
1127
1128 /* Project verts to 2D. */
1129 axis_dominant_v3_to_m3_negate(axis_mat, normal);
1130
1131 for (j = 0; j < mp_totloop; j++) {
1132 const int vert = corner_verts[mp_loopstart + j];
1133 mul_v2_m3v3(projverts[j], axis_mat, positions[vert]);
1134 }
1135
1136 BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena);
1137
1138 /* Apply fill. */
1139 for (j = 0; j < totfilltri; j++) {
1140 uint *tri = tris[j];
1141 lidx = lindices[mface_index];
1142
1143 mface_to_poly_map[mface_index] = poly_index;
1144 mf = &mface[mface_index];
1145
1146 /* Set loop indices, transformed to vert indices later. */
1147 l1 = mp_loopstart + tri[0];
1148 l2 = mp_loopstart + tri[1];
1149 l3 = mp_loopstart + tri[2];
1150
1151 mf->v1 = corner_verts[l1];
1152 mf->v2 = corner_verts[l2];
1153 mf->v3 = corner_verts[l3];
1154 mf->v4 = 0;
1155
1156 lidx[0] = l1;
1157 lidx[1] = l2;
1158 lidx[2] = l3;
1159 lidx[3] = 0;
1160
1161 mf->mat_nr = material_indices ? material_indices[poly_index] : 0;
1162 mf->edcode = 0;
1163
1164 mface_index++;
1165 }
1166
1167 BLI_memarena_clear(arena);
1168 }
1169 }
1170
1171 if (arena) {
1172 BLI_memarena_free(arena);
1173 arena = nullptr;
1174 }
1175
1176 CustomData_free(fdata_legacy);
1177 totface = mface_index;
1178
1179 BLI_assert(totface <= corner_tris_num);
1180
1181 /* Not essential but without this we store over-allocated memory in the #CustomData layers. */
1182 if (LIKELY(corner_tris_num != totface)) {
1183 mface = (MFace *)MEM_reallocN(mface, sizeof(*mface) * size_t(totface));
1184 mface_to_poly_map = (int *)MEM_reallocN(mface_to_poly_map,
1185 sizeof(*mface_to_poly_map) * size_t(totface));
1186 }
1187
1188 CustomData_add_layer_with_data(fdata_legacy, CD_MFACE, mface, totface, nullptr);
1189
1190 /* #CD_ORIGINDEX will contain an array of indices from tessellation-faces to the polygons
1191 * they are directly tessellated from. */
1192 CustomData_add_layer_with_data(fdata_legacy, CD_ORIGINDEX, mface_to_poly_map, totface, nullptr);
1193 add_mface_layers(mesh, fdata_legacy, ldata, totface);
1194
1195 /* NOTE: quad detection issue - fourth vertex-index vs fourth loop-index:
1196 * Polygons take care of their loops ordering, hence not of their vertices ordering.
1197 * Currently, the #TFace fourth vertex index might be 0 even for a quad.
1198 * However, we know our fourth loop index is never 0 for quads
1199 * (because they are sorted for polygons, and our quads are still mere copies of their polygons).
1200 * So we pass nullptr as #MFace pointer, and #mesh_loops_to_tessdata
1201 * will use the fourth loop index as quad test. */
1202 mesh_loops_to_tessdata(fdata_legacy, ldata, nullptr, mface_to_poly_map, lindices, totface);
1203
1204 /* NOTE: quad detection issue - fourth vert-index vs fourth loop-index:
1205 * ...However, most #TFace code uses `MFace->v4 == 0` test to check whether it is a tri or quad.
1206 * BKE_mesh_mface_index_validate() will check this and rotate the tessellated face if needed.
1207 */
1208#ifdef USE_TESSFACE_QUADS
1209 mf = mface;
1210 for (mface_index = 0; mface_index < totface; mface_index++, mf++) {
1211 if (mf->edcode == TESSFACE_IS_QUAD) {
1212 BKE_mesh_mface_index_validate(mf, fdata_legacy, mface_index, 4);
1213 mf->edcode = 0;
1214 }
1215 }
1216#endif
1217
1218 MEM_freeN(lindices);
1219
1220 return totface;
1221
1222#undef USE_TESSFACE_SPEEDUP
1223#undef USE_TESSFACE_QUADS
1224
1225#undef ML_TO_MF
1226#undef ML_TO_MF_QUAD
1227}
1228
1230{
1232 *mesh,
1233 &mesh->fdata_legacy,
1234 &mesh->corner_data,
1235 &mesh->face_data,
1236 reinterpret_cast<float (*)[3]>(mesh->vert_positions_for_write().data()),
1237 mesh->totface_legacy,
1238 mesh->corners_num,
1239 mesh->faces_num);
1240
1242}
1243
1245{
1246 if (mesh->faces_num && mesh->totface_legacy == 0) {
1248 }
1249}
1250
1252
1253/* -------------------------------------------------------------------- */
1256
1258{
1259 using namespace blender;
1260 using namespace blender::bke;
1261 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1262 if (attributes.contains("sharp_face") || !CustomData_get_layer(&mesh->face_data, CD_MPOLY)) {
1263 return;
1264 }
1265 const Span<MPoly> polys(
1266 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1267 mesh->faces_num);
1268 if (std::any_of(polys.begin(), polys.end(), [](const MPoly &poly) {
1269 return !(poly.flag_legacy & ME_SMOOTH);
1270 }))
1271 {
1272 SpanAttributeWriter<bool> sharp_faces = attributes.lookup_or_add_for_write_only_span<bool>(
1273 "sharp_face", AttrDomain::Face);
1274 threading::parallel_for(polys.index_range(), 4096, [&](const IndexRange range) {
1275 for (const int i : range) {
1276 sharp_faces.span[i] = !(polys[i].flag_legacy & ME_SMOOTH);
1277 }
1278 });
1279 sharp_faces.finish();
1280 }
1281 else {
1282 attributes.remove("sharp_face");
1283 }
1284}
1285
1287
1288/* -------------------------------------------------------------------- */
1291
1293{
1294 using namespace blender;
1295 if (mesh->attributes().contains(".sculpt_face_set")) {
1296 return;
1297 }
1298 void *faceset_data = nullptr;
1299 const ImplicitSharingInfo *faceset_sharing_info = nullptr;
1300 for (const int i : IndexRange(mesh->face_data.totlayer)) {
1301 CustomDataLayer &layer = mesh->face_data.layers[i];
1302 if (layer.type == CD_SCULPT_FACE_SETS) {
1303 faceset_data = layer.data;
1304 faceset_sharing_info = layer.sharing_info;
1305 layer.data = nullptr;
1306 layer.sharing_info = nullptr;
1307 CustomData_free_layer(&mesh->face_data, CD_SCULPT_FACE_SETS, i);
1308 break;
1309 }
1310 }
1311 if (faceset_data != nullptr) {
1314 faceset_data,
1315 mesh->faces_num,
1316 ".sculpt_face_set",
1317 faceset_sharing_info);
1318 }
1319 if (faceset_sharing_info != nullptr) {
1320 faceset_sharing_info->remove_user_and_delete_if_last();
1321 }
1322}
1323
1325
1326/* -------------------------------------------------------------------- */
1329
1331{
1332 using namespace blender;
1333 if (mesh->attributes().contains("face_maps")) {
1334 return;
1335 }
1336 int *data = nullptr;
1337 const ImplicitSharingInfo *sharing_info = nullptr;
1338 for (const int i : IndexRange(mesh->face_data.totlayer)) {
1339 CustomDataLayer &layer = mesh->face_data.layers[i];
1340 if (layer.type == CD_FACEMAP) {
1341 data = static_cast<int *>(layer.data);
1342 sharing_info = layer.sharing_info;
1343 layer.data = nullptr;
1344 layer.sharing_info = nullptr;
1345 CustomData_free_layer(&mesh->face_data, CD_FACEMAP, i);
1346 break;
1347 }
1348 }
1349 if (!data) {
1350 return;
1351 }
1352
1354 &mesh->face_data, CD_PROP_INT32, data, mesh->faces_num, "face_maps", sharing_info);
1355 if (sharing_info != nullptr) {
1356 sharing_info->remove_user_and_delete_if_last();
1357 }
1358
1360 for (const int i : IndexRange(mesh->faces_num)) {
1361 if (data[i] == -1) {
1362 /* -1 values "didn't have" a face map. */
1363 continue;
1364 }
1365 groups.add(data[i], i);
1366 }
1367
1368 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1369 for (const auto item : groups.items()) {
1371 ".temp_face_map_" + std::to_string(item.key), bke::AttrDomain::Face);
1372 if (attribute) {
1373 attribute.span.fill_indices(item.value.as_span(), true);
1374 attribute.finish();
1375 }
1376 }
1377}
1378
1380{
1381 LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
1383 }
1384
1385 LISTBASE_FOREACH (Object *, object, &bmain->objects) {
1386 if (object->type != OB_MESH) {
1387 continue;
1388 }
1389 Mesh *mesh = static_cast<Mesh *>(object->data);
1390 int i;
1391 LISTBASE_FOREACH_INDEX (bFaceMap *, face_map, &object->fmaps, i) {
1392 mesh->attributes_for_write().rename(".temp_face_map_" + std::to_string(i), face_map->name);
1393 }
1394 BLI_freelistN(&object->fmaps);
1395 }
1396}
1397
1399
1400/* -------------------------------------------------------------------- */
1403
1405{
1406 using namespace blender;
1407 if (mesh->mvert && !CustomData_has_layer(&mesh->vert_data, CD_BWEIGHT)) {
1408 const Span<MVert> verts(mesh->mvert, mesh->verts_num);
1409 if (mesh->cd_flag & ME_CDFLAG_VERT_BWEIGHT) {
1410 float *weights = static_cast<float *>(
1411 CustomData_add_layer(&mesh->vert_data, CD_BWEIGHT, CD_CONSTRUCT, verts.size()));
1412 for (const int i : verts.index_range()) {
1413 weights[i] = verts[i].bweight_legacy / 255.0f;
1414 }
1415 }
1416 }
1417
1418 if (mesh->medge && !CustomData_has_layer(&mesh->edge_data, CD_BWEIGHT)) {
1419 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1420 if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
1421 float *weights = static_cast<float *>(
1422 CustomData_add_layer(&mesh->edge_data, CD_BWEIGHT, CD_CONSTRUCT, edges.size()));
1423 for (const int i : edges.index_range()) {
1424 weights[i] = edges[i].bweight_legacy / 255.0f;
1425 }
1426 }
1427 }
1428}
1429
1431 const eCustomDataType old_type,
1432 const eCustomDataType new_type,
1433 const int elems_num,
1434 const char *new_name)
1435{
1436 using namespace blender;
1437 void *data = nullptr;
1438 const ImplicitSharingInfo *sharing_info = nullptr;
1439 for (const int i : IndexRange(custom_data.totlayer)) {
1440 CustomDataLayer &layer = custom_data.layers[i];
1441 if (layer.type == old_type) {
1442 data = layer.data;
1443 sharing_info = layer.sharing_info;
1444 layer.data = nullptr;
1445 layer.sharing_info = nullptr;
1446 CustomData_free_layer(&custom_data, old_type, i);
1447 break;
1448 }
1449 }
1450 if (data != nullptr) {
1452 &custom_data, new_type, data, elems_num, new_name, sharing_info);
1453 }
1454 if (sharing_info != nullptr) {
1455 sharing_info->remove_user_and_delete_if_last();
1456 }
1457}
1458
1460{
1461 if (!mesh->attributes().contains("bevel_weight_vert")) {
1463 mesh->vert_data, CD_BWEIGHT, CD_PROP_FLOAT, mesh->verts_num, "bevel_weight_vert");
1464 }
1465 if (!mesh->attributes().contains("bevel_weight_edge")) {
1467 mesh->edge_data, CD_BWEIGHT, CD_PROP_FLOAT, mesh->edges_num, "bevel_weight_edge");
1468 }
1469}
1470
1472
1473/* -------------------------------------------------------------------- */
1476
1478{
1479 using namespace blender;
1480 if (!mesh->medge) {
1481 return;
1482 }
1483 if (CustomData_has_layer(&mesh->edge_data, CD_CREASE)) {
1484 return;
1485 }
1486 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1487 if (mesh->cd_flag & ME_CDFLAG_EDGE_CREASE) {
1488 float *creases = static_cast<float *>(
1489 CustomData_add_layer(&mesh->edge_data, CD_CREASE, CD_CONSTRUCT, edges.size()));
1490 for (const int i : edges.index_range()) {
1491 creases[i] = edges[i].crease_legacy / 255.0f;
1492 }
1493 }
1494}
1495
1497{
1498 if (!mesh->attributes().contains("crease_vert")) {
1500 mesh->vert_data, CD_CREASE, CD_PROP_FLOAT, mesh->verts_num, "crease_vert");
1501 }
1502 if (!mesh->attributes().contains("crease_edge")) {
1504 mesh->edge_data, CD_CREASE, CD_PROP_FLOAT, mesh->edges_num, "crease_edge");
1505 }
1506}
1507
1509
1510/* -------------------------------------------------------------------- */
1513
1515{
1516 using namespace blender;
1517 using namespace blender::bke;
1518 if (!mesh->medge) {
1519 return;
1520 }
1521 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1522 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1523 if (attributes.contains("sharp_edge")) {
1524 return;
1525 }
1526 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1527 return edge.flag_legacy & ME_SHARP;
1528 }))
1529 {
1530 SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_only_span<bool>(
1531 "sharp_edge", AttrDomain::Edge);
1532 threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) {
1533 for (const int i : range) {
1534 sharp_edges.span[i] = edges[i].flag_legacy & ME_SHARP;
1535 }
1536 });
1537 sharp_edges.finish();
1538 }
1539}
1540
1542
1543/* -------------------------------------------------------------------- */
1546
1548{
1549 using namespace blender;
1550 using namespace blender::bke;
1551 if (!mesh->medge) {
1552 return;
1553 }
1554 MutableSpan<MEdge> edges(mesh->medge, mesh->edges_num);
1555 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1556 if (attributes.contains(".uv_seam")) {
1557 return;
1558 }
1559 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1560 return edge.flag_legacy & ME_SEAM;
1561 }))
1562 {
1564 ".uv_seam", AttrDomain::Edge);
1565 threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) {
1566 for (const int i : range) {
1567 uv_seams.span[i] = edges[i].flag_legacy & ME_SEAM;
1568 }
1569 });
1570 uv_seams.finish();
1571 }
1572}
1573
1575
1576/* -------------------------------------------------------------------- */
1579
1581{
1582 using namespace blender;
1583 using namespace blender::bke;
1584 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1585 if (!mesh->mvert || attributes.contains(".hide_vert") || attributes.contains(".hide_edge") ||
1586 attributes.contains(".hide_poly"))
1587 {
1588 return;
1589 }
1590 const Span<MVert> verts(mesh->mvert, mesh->verts_num);
1591 if (std::any_of(verts.begin(), verts.end(), [](const MVert &vert) {
1592 return vert.flag_legacy & ME_HIDE;
1593 }))
1594 {
1596 ".hide_vert", AttrDomain::Point);
1597 threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
1598 for (const int i : range) {
1599 hide_vert.span[i] = verts[i].flag_legacy & ME_HIDE;
1600 }
1601 });
1602 hide_vert.finish();
1603 }
1604
1605 if (mesh->medge) {
1606 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1607 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1608 return edge.flag_legacy & ME_HIDE;
1609 }))
1610 {
1611 SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
1612 ".hide_edge", AttrDomain::Edge);
1613 threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
1614 for (const int i : range) {
1615 hide_edge.span[i] = edges[i].flag_legacy & ME_HIDE;
1616 }
1617 });
1618 hide_edge.finish();
1619 }
1620 }
1621
1622 const Span<MPoly> polys(
1623 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1624 mesh->faces_num);
1625 if (std::any_of(polys.begin(), polys.end(), [](const MPoly &poly) {
1626 return poly.flag_legacy & ME_HIDE;
1627 }))
1628 {
1629 SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>(
1630 ".hide_poly", AttrDomain::Face);
1631 threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
1632 for (const int i : range) {
1633 hide_poly.span[i] = polys[i].flag_legacy & ME_HIDE;
1634 }
1635 });
1636 hide_poly.finish();
1637 }
1638}
1639
1641
1642/* -------------------------------------------------------------------- */
1645
1647{
1648 using namespace blender;
1649 using namespace blender::bke;
1650 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1651 if (!CustomData_has_layer(&mesh->face_data, CD_MPOLY) || attributes.contains("material_index")) {
1652 return;
1653 }
1654 const Span<MPoly> polys(
1655 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1656 mesh->faces_num);
1657 if (std::any_of(
1658 polys.begin(), polys.end(), [](const MPoly &poly) { return poly.mat_nr_legacy != 0; }))
1659 {
1660 SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>(
1661 "material_index", AttrDomain::Face);
1662 threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
1663 for (const int i : range) {
1664 material_indices.span[i] = polys[i].mat_nr_legacy;
1665 }
1666 });
1667 material_indices.finish();
1668 }
1669}
1670
1672
1673/* -------------------------------------------------------------------- */
1676
1678{
1679 using namespace blender;
1680 using namespace blender::bke;
1681 if (!CustomData_has_layer(&mesh->corner_data, CD_MLOOPUV)) {
1682 return;
1683 }
1684
1685 /* Store layer names since they will be removed, used to set the active status of new layers.
1686 * Use intermediate #StringRef because the names can be null. */
1687
1688 Array<std::string> uv_names(CustomData_number_of_layers(&mesh->corner_data, CD_MLOOPUV));
1689 for (const int i : uv_names.index_range()) {
1690 uv_names[i] = CustomData_get_layer_name(&mesh->corner_data, CD_MLOOPUV, i);
1691 }
1692 const int active_name_i = uv_names.as_span().first_index_try(
1693 StringRef(CustomData_get_active_layer_name(&mesh->corner_data, CD_MLOOPUV)));
1694 const int default_name_i = uv_names.as_span().first_index_try(
1695 StringRef(CustomData_get_render_layer_name(&mesh->corner_data, CD_MLOOPUV)));
1696
1697 for (const int i : uv_names.index_range()) {
1698 const MLoopUV *mloopuv = static_cast<const MLoopUV *>(
1699 CustomData_get_layer_named(&mesh->corner_data, CD_MLOOPUV, uv_names[i]));
1700 const uint32_t needed_boolean_attributes = threading::parallel_reduce(
1701 IndexRange(mesh->corners_num),
1702 4096,
1703 0,
1704 [&](const IndexRange range, uint32_t init) {
1705 for (const int i : range) {
1706 init |= mloopuv[i].flag;
1707 }
1708 return init;
1709 },
1710 [](const uint32_t a, const uint32_t b) { return a | b; });
1711
1712 float2 *coords = MEM_malloc_arrayN<float2>(size_t(mesh->corners_num), __func__);
1713 bool *pin = nullptr;
1714 if (needed_boolean_attributes & MLOOPUV_PINNED) {
1715 pin = MEM_malloc_arrayN<bool>(size_t(mesh->corners_num), __func__);
1716 }
1717
1718 threading::parallel_for(IndexRange(mesh->corners_num), 4096, [&](IndexRange range) {
1719 for (const int i : range) {
1720 coords[i] = mloopuv[i].uv;
1721 }
1722 if (pin) {
1723 for (const int i : range) {
1724 pin[i] = mloopuv[i].flag & MLOOPUV_PINNED;
1725 }
1726 }
1727 });
1728
1729 CustomData_free_layer_named(&mesh->corner_data, uv_names[i]);
1730
1732 const std::string new_name = BKE_attribute_calc_unique_name(owner, uv_names[i].c_str());
1733 uv_names[i] = new_name;
1734
1736 &mesh->corner_data, CD_PROP_FLOAT2, coords, mesh->corners_num, new_name, nullptr);
1737 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
1738 if (pin) {
1741 pin,
1742 mesh->corners_num,
1743 BKE_uv_map_pin_name_get(new_name, buffer),
1744 nullptr);
1745 }
1746 }
1747
1748 if (active_name_i != -1) {
1753 uv_names[active_name_i]));
1754 }
1755 if (default_name_i != -1) {
1760 uv_names[default_name_i]));
1761 }
1762}
1763
1765
1768
1770{
1771 using namespace blender;
1772 using namespace blender::bke;
1773 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1774 if (!mesh->mvert || attributes.contains(".select_vert") || attributes.contains(".select_edge") ||
1775 attributes.contains(".select_poly"))
1776 {
1777 return;
1778 }
1779
1780 const Span<MVert> verts(mesh->mvert, mesh->verts_num);
1781 if (std::any_of(
1782 verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag_legacy & SELECT; }))
1783 {
1784 SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_only_span<bool>(
1785 ".select_vert", AttrDomain::Point);
1786 threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
1787 for (const int i : range) {
1788 select_vert.span[i] = verts[i].flag_legacy & SELECT;
1789 }
1790 });
1791 select_vert.finish();
1792 }
1793
1794 if (mesh->medge) {
1795 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1796 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1797 return edge.flag_legacy & SELECT;
1798 }))
1799 {
1800 SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_only_span<bool>(
1801 ".select_edge", AttrDomain::Edge);
1802 threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
1803 for (const int i : range) {
1804 select_edge.span[i] = edges[i].flag_legacy & SELECT;
1805 }
1806 });
1807 select_edge.finish();
1808 }
1809 }
1810
1811 const Span<MPoly> polys(
1812 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1813 mesh->faces_num);
1814 if (std::any_of(polys.begin(), polys.end(), [](const MPoly &poly) {
1815 return poly.flag_legacy & ME_FACE_SEL;
1816 }))
1817 {
1818 SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_only_span<bool>(
1819 ".select_poly", AttrDomain::Face);
1820 threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
1821 for (const int i : range) {
1822 select_poly.span[i] = polys[i].flag_legacy & ME_FACE_SEL;
1823 }
1824 });
1825 select_poly.finish();
1826 }
1827}
1828
1830
1831/* -------------------------------------------------------------------- */
1834
1836{
1837 using namespace blender;
1838 using namespace blender::bke;
1839 const MVert *mvert = static_cast<const MVert *>(
1840 CustomData_get_layer(&mesh->vert_data, CD_MVERT));
1841 if (!mvert || CustomData_has_layer_named(&mesh->vert_data, CD_PROP_FLOAT3, "position")) {
1842 return;
1843 }
1844
1845 const Span<MVert> verts(mvert, mesh->verts_num);
1846 MutableSpan<float3> positions(
1847 static_cast<float3 *>(CustomData_add_layer_named(
1848 &mesh->vert_data, CD_PROP_FLOAT3, CD_CONSTRUCT, mesh->verts_num, "position")),
1849 mesh->verts_num);
1850 threading::parallel_for(verts.index_range(), 2048, [&](IndexRange range) {
1851 for (const int i : range) {
1852 positions[i] = verts[i].co_legacy;
1853 }
1854 });
1855
1856 CustomData_free_layers(&mesh->vert_data, CD_MVERT);
1857 mesh->mvert = nullptr;
1858}
1859
1861
1862/* -------------------------------------------------------------------- */
1865
1867{
1868 using namespace blender;
1869 using namespace blender::bke;
1870 const MEdge *medge = static_cast<const MEdge *>(
1871 CustomData_get_layer(&mesh->edge_data, CD_MEDGE));
1872 if (!medge || CustomData_has_layer_named(&mesh->edge_data, CD_PROP_INT32_2D, ".edge_verts")) {
1873 return;
1874 }
1875
1876 const Span<MEdge> legacy_edges(medge, mesh->edges_num);
1877 MutableSpan<int2> edges(
1878 static_cast<int2 *>(CustomData_add_layer_named(
1879 &mesh->edge_data, CD_PROP_INT32_2D, CD_CONSTRUCT, mesh->edges_num, ".edge_verts")),
1880 mesh->edges_num);
1881 threading::parallel_for(legacy_edges.index_range(), 2048, [&](IndexRange range) {
1882 for (const int i : range) {
1883 edges[i] = int2(legacy_edges[i].v1, legacy_edges[i].v2);
1884 }
1885 });
1886
1887 CustomData_free_layers(&mesh->edge_data, CD_MEDGE);
1888 mesh->medge = nullptr;
1889}
1890
1892
1893/* -------------------------------------------------------------------- */
1896
1898{
1899 using namespace blender;
1900 /* It's not clear whether the active/render status was stored in the dedicated flags or in the
1901 * generic CustomData layer indices, so convert from both, preferring the explicit flags. */
1902
1903 auto active_from_flags = [&](const CustomData &data) {
1904 if (!mesh->active_color_attribute) {
1905 for (const int i : IndexRange(data.totlayer)) {
1906 if (data.layers[i].flag & CD_FLAG_COLOR_ACTIVE) {
1907 mesh->active_color_attribute = BLI_strdup(data.layers[i].name);
1908 }
1909 }
1910 }
1911 };
1912 auto active_from_indices = [&](const CustomData &data) {
1913 if (!mesh->active_color_attribute) {
1915 if (i != -1) {
1916 mesh->active_color_attribute = BLI_strdup(data.layers[i].name);
1917 }
1918 }
1919 if (!mesh->active_color_attribute) {
1921 if (i != -1) {
1922 mesh->active_color_attribute = BLI_strdup(data.layers[i].name);
1923 }
1924 }
1925 };
1926 auto default_from_flags = [&](const CustomData &data) {
1927 if (!mesh->default_color_attribute) {
1928 for (const int i : IndexRange(data.totlayer)) {
1929 if (data.layers[i].flag & CD_FLAG_COLOR_RENDER) {
1930 mesh->default_color_attribute = BLI_strdup(data.layers[i].name);
1931 }
1932 }
1933 }
1934 };
1935 auto default_from_indices = [&](const CustomData &data) {
1936 if (!mesh->default_color_attribute) {
1938 if (i != -1) {
1939 mesh->default_color_attribute = BLI_strdup(data.layers[i].name);
1940 }
1941 }
1942 if (!mesh->default_color_attribute) {
1944 if (i != -1) {
1945 mesh->default_color_attribute = BLI_strdup(data.layers[i].name);
1946 }
1947 }
1948 };
1949
1950 active_from_flags(mesh->vert_data);
1951 active_from_flags(mesh->corner_data);
1952 active_from_indices(mesh->vert_data);
1953 active_from_indices(mesh->corner_data);
1954
1955 default_from_flags(mesh->vert_data);
1956 default_from_flags(mesh->corner_data);
1957 default_from_indices(mesh->vert_data);
1958 default_from_indices(mesh->corner_data);
1959}
1960
1962
1963/* -------------------------------------------------------------------- */
1966
1968{
1969 using namespace blender;
1970 if (CustomData_has_layer_named(&mesh->corner_data, CD_PROP_INT32, ".corner_vert") &&
1971 CustomData_has_layer_named(&mesh->corner_data, CD_PROP_INT32, ".corner_edge"))
1972 {
1973 return;
1974 }
1975 const Span<MLoop> loops(
1976 static_cast<const MLoop *>(CustomData_get_layer(&mesh->corner_data, CD_MLOOP)),
1977 mesh->corners_num);
1978 MutableSpan<int> corner_verts(
1979 static_cast<int *>(CustomData_add_layer_named(
1980 &mesh->corner_data, CD_PROP_INT32, CD_CONSTRUCT, mesh->corners_num, ".corner_vert")),
1981 mesh->corners_num);
1982 MutableSpan<int> corner_edges(
1983 static_cast<int *>(CustomData_add_layer_named(
1984 &mesh->corner_data, CD_PROP_INT32, CD_CONSTRUCT, mesh->corners_num, ".corner_edge")),
1985 mesh->corners_num);
1986 threading::parallel_for(loops.index_range(), 2048, [&](IndexRange range) {
1987 for (const int i : range) {
1988 corner_verts[i] = loops[i].v;
1989 corner_edges[i] = loops[i].e;
1990 }
1991 });
1992
1993 CustomData_free_layers(&mesh->corner_data, CD_MLOOP);
1994}
1995
1997
1998/* -------------------------------------------------------------------- */
2001
2002static bool poly_loops_orders_match(const Span<MPoly> polys)
2003{
2004 for (const int i : polys.index_range().drop_back(1)) {
2005 if (polys[i].loopstart > polys[i + 1].loopstart) {
2006 return false;
2007 }
2008 }
2009 return true;
2010}
2011
2013{
2014 using namespace blender;
2015 if (mesh->face_offset_indices) {
2016 return;
2017 }
2018 const Span<MPoly> polys(
2019 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
2020 mesh->faces_num);
2021
2023 MutableSpan<int> offsets = mesh->face_offsets_for_write();
2024
2025 if (poly_loops_orders_match(polys)) {
2026 for (const int i : polys.index_range()) {
2027 offsets[i] = polys[i].loopstart;
2028 }
2029 }
2030 else {
2031 /* Reorder mesh polygons to match the order of their loops. */
2032 Array<int> orig_indices(polys.size());
2034 std::stable_sort(orig_indices.begin(), orig_indices.end(), [polys](const int a, const int b) {
2035 return polys[a].loopstart < polys[b].loopstart;
2036 });
2037 CustomData old_poly_data = mesh->face_data;
2038 CustomData_reset(&mesh->face_data);
2040 &old_poly_data, &mesh->face_data, CD_MASK_MESH.pmask, CD_CONSTRUCT, mesh->faces_num);
2041
2042 int offset = 0;
2043 for (const int i : orig_indices.index_range()) {
2044 offsets[i] = offset;
2045 offset += polys[orig_indices[i]].totloop;
2046 }
2047
2048 threading::parallel_for(orig_indices.index_range(), 1024, [&](const IndexRange range) {
2049 for (const int i : range) {
2050 CustomData_copy_data(&old_poly_data, &mesh->face_data, orig_indices[i], i, 1);
2051 }
2052 });
2053
2054 CustomData_free(&old_poly_data);
2055 }
2056
2057 CustomData_free_layers(&mesh->face_data, CD_MPOLY);
2058}
2059
2061
2062/* -------------------------------------------------------------------- */
2065
2066namespace blender::bke {
2067
2068static bNodeTree *add_auto_smooth_node_tree(Main &bmain, Library *owner_library)
2069{
2071 &bmain, owner_library, DATA_("Auto Smooth"), "GeometryNodeTree");
2072 if (!group->geometry_node_asset_traits) {
2074 }
2076
2077 group->tree_interface.add_socket(
2078 DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
2079 group->tree_interface.add_socket(
2080 DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
2081 bNodeTreeInterfaceSocket *angle_io_socket = group->tree_interface.add_socket(
2082 DATA_("Angle"), "", "NodeSocketFloat", NODE_INTERFACE_SOCKET_INPUT, nullptr);
2083 auto &angle_data = *static_cast<bNodeSocketValueFloat *>(angle_io_socket->socket_data);
2084 angle_data.min = 0.0f;
2085 angle_data.max = DEG2RADF(180.0f);
2086 angle_data.subtype = PROP_ANGLE;
2087
2088 bNode *group_output = node_add_node(nullptr, *group, "NodeGroupOutput");
2089 group_output->location[0] = 480.0f;
2090 group_output->location[1] = -100.0f;
2091 bNode *group_input_angle = node_add_node(nullptr, *group, "NodeGroupInput");
2092 group_input_angle->location[0] = -420.0f;
2093 group_input_angle->location[1] = -300.0f;
2094 LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_angle->outputs) {
2095 if (!STREQ(socket->identifier, "Socket_2")) {
2096 socket->flag |= SOCK_HIDDEN;
2097 }
2098 }
2099 bNode *group_input_mesh = node_add_node(nullptr, *group, "NodeGroupInput");
2100 group_input_mesh->location[0] = -60.0f;
2101 group_input_mesh->location[1] = -100.0f;
2102 LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_mesh->outputs) {
2103 if (!STREQ(socket->identifier, "Socket_1")) {
2104 socket->flag |= SOCK_HIDDEN;
2105 }
2106 }
2107 bNode *shade_smooth_edge = node_add_node(nullptr, *group, "GeometryNodeSetShadeSmooth");
2108 shade_smooth_edge->custom1 = int16_t(bke::AttrDomain::Edge);
2109 shade_smooth_edge->location[0] = 120.0f;
2110 shade_smooth_edge->location[1] = -100.0f;
2111 bNode *shade_smooth_face = node_add_node(nullptr, *group, "GeometryNodeSetShadeSmooth");
2112 shade_smooth_face->custom1 = int16_t(bke::AttrDomain::Face);
2113 shade_smooth_face->location[0] = 300.0f;
2114 shade_smooth_face->location[1] = -100.0f;
2115 bNode *edge_angle = node_add_node(nullptr, *group, "GeometryNodeInputMeshEdgeAngle");
2116 edge_angle->location[0] = -420.0f;
2117 edge_angle->location[1] = -220.0f;
2118 bNode *edge_smooth = node_add_node(nullptr, *group, "GeometryNodeInputEdgeSmooth");
2119 edge_smooth->location[0] = -60.0f;
2120 edge_smooth->location[1] = -160.0f;
2121 bNode *face_smooth = node_add_node(nullptr, *group, "GeometryNodeInputShadeSmooth");
2122 face_smooth->location[0] = -240.0f;
2123 face_smooth->location[1] = -340.0f;
2124 bNode *boolean_and = node_add_node(nullptr, *group, "FunctionNodeBooleanMath");
2125 boolean_and->custom1 = NODE_BOOLEAN_MATH_AND;
2126 boolean_and->location[0] = -60.0f;
2127 boolean_and->location[1] = -220.0f;
2128 bNode *less_than_or_equal = node_add_node(nullptr, *group, "FunctionNodeCompare");
2129 static_cast<NodeFunctionCompare *>(less_than_or_equal->storage)->operation =
2131 less_than_or_equal->location[0] = -240.0f;
2132 less_than_or_equal->location[1] = -180.0f;
2133
2134 node_add_link(*group,
2135 *edge_angle,
2136 *node_find_socket(*edge_angle, SOCK_OUT, "Unsigned Angle"),
2137 *less_than_or_equal,
2138 *node_find_socket(*less_than_or_equal, SOCK_IN, "A"));
2139 node_add_link(*group,
2140 *shade_smooth_face,
2141 *node_find_socket(*shade_smooth_face, SOCK_OUT, "Geometry"),
2142 *group_output,
2143 *node_find_socket(*group_output, SOCK_IN, "Socket_0"));
2144 node_add_link(*group,
2145 *group_input_angle,
2146 *node_find_socket(*group_input_angle, SOCK_OUT, "Socket_2"),
2147 *less_than_or_equal,
2148 *node_find_socket(*less_than_or_equal, SOCK_IN, "B"));
2149 node_add_link(*group,
2150 *less_than_or_equal,
2151 *node_find_socket(*less_than_or_equal, SOCK_OUT, "Result"),
2152 *boolean_and,
2153 *node_find_socket(*boolean_and, SOCK_IN, "Boolean"));
2154 node_add_link(*group,
2155 *face_smooth,
2156 *node_find_socket(*face_smooth, SOCK_OUT, "Smooth"),
2157 *boolean_and,
2158 *node_find_socket(*boolean_and, SOCK_IN, "Boolean_001"));
2159 node_add_link(*group,
2160 *group_input_mesh,
2161 *node_find_socket(*group_input_mesh, SOCK_OUT, "Socket_1"),
2162 *shade_smooth_edge,
2163 *node_find_socket(*shade_smooth_edge, SOCK_IN, "Geometry"));
2164 node_add_link(*group,
2165 *edge_smooth,
2166 *node_find_socket(*edge_smooth, SOCK_OUT, "Smooth"),
2167 *shade_smooth_edge,
2168 *node_find_socket(*shade_smooth_edge, SOCK_IN, "Selection"));
2169 node_add_link(*group,
2170 *shade_smooth_edge,
2171 *node_find_socket(*shade_smooth_edge, SOCK_OUT, "Geometry"),
2172 *shade_smooth_face,
2173 *node_find_socket(*shade_smooth_face, SOCK_IN, "Geometry"));
2174 node_add_link(*group,
2175 *boolean_and,
2176 *node_find_socket(*boolean_and, SOCK_OUT, "Boolean"),
2177 *shade_smooth_edge,
2178 *node_find_socket(*shade_smooth_edge, SOCK_IN, "Shade Smooth"));
2179
2180 LISTBASE_FOREACH (bNode *, node, &group->nodes) {
2181 node_set_selected(*node, false);
2182 }
2183
2185
2186 return group;
2187}
2188
2190{
2192 for (const bNode *node : nodes) {
2193 LISTBASE_FOREACH (const bNodeSocket *, socket, &node->inputs) {
2194 result.add_new(socket);
2195 }
2196 LISTBASE_FOREACH (const bNodeSocket *, socket, &node->outputs) {
2197 result.add_new(socket);
2198 }
2199 }
2200 return result;
2201}
2202
2203/* Checks if the node group is the same as the one generated by #create_auto_smooth_modifier. */
2204static bool is_auto_smooth_node_tree(const bNodeTree &group)
2205{
2206 if (group.type != NTREE_GEOMETRY) {
2207 return false;
2208 }
2209 const Span<const bNode *> nodes = group.all_nodes();
2210 if (nodes.size() != 10) {
2211 return false;
2212 }
2213 if (!group.geometry_node_asset_traits) {
2214 return false;
2215 }
2217 return false;
2218 }
2219 const std::array<StringRef, 10> idnames({"NodeGroupOutput",
2220 "NodeGroupInput",
2221 "NodeGroupInput",
2222 "GeometryNodeSetShadeSmooth",
2223 "GeometryNodeSetShadeSmooth",
2224 "GeometryNodeInputMeshEdgeAngle",
2225 "GeometryNodeInputEdgeSmooth",
2226 "GeometryNodeInputShadeSmooth",
2227 "FunctionNodeBooleanMath",
2228 "FunctionNodeCompare"});
2229 for (const int i : nodes.index_range()) {
2230 if (nodes[i]->idname != idnames[i]) {
2231 return false;
2232 }
2233 }
2234 if (nodes[3]->custom1 != int16_t(bke::AttrDomain::Edge)) {
2235 return false;
2236 }
2237 if (static_cast<bNodeSocket *>(nodes[4]->inputs.last)
2238 ->default_value_typed<bNodeSocketValueBoolean>()
2239 ->value != 1)
2240 {
2241 return false;
2242 }
2243 if (nodes[4]->custom1 != int16_t(bke::AttrDomain::Face)) {
2244 return false;
2245 }
2246 if (nodes[8]->custom1 != NODE_BOOLEAN_MATH_AND) {
2247 return false;
2248 }
2249 if (static_cast<NodeFunctionCompare *>(nodes[9]->storage)->operation != NODE_COMPARE_LESS_EQUAL)
2250 {
2251 return false;
2252 }
2253 if (BLI_listbase_count(&group.links) != 9) {
2254 return false;
2255 }
2256
2257 const std::array<int, 9> link_from_socket_indices({16, 15, 3, 36, 19, 5, 18, 11, 22});
2258 const std::array<int, 9> link_to_socket_indices({23, 0, 24, 20, 21, 8, 9, 12, 10});
2260 int i;
2261 LISTBASE_FOREACH_INDEX (const bNodeLink *, link, &group.links, i) {
2262 if (socket_indices.index_of(link->fromsock) != link_from_socket_indices[i]) {
2263 return false;
2264 }
2265 if (socket_indices.index_of(link->tosock) != link_to_socket_indices[i]) {
2266 return false;
2267 }
2268 }
2269
2270 return true;
2271}
2272
2274 Object &object,
2275 const FunctionRef<bNodeTree *(Library *owner_library)> get_node_group,
2276 const float angle)
2277{
2278 auto *md = reinterpret_cast<NodesModifierData *>(BKE_modifier_new(eModifierType_Nodes));
2279 STRNCPY_UTF8(md->modifier.name, DATA_("Auto Smooth"));
2280 BKE_modifier_unique_name(&object.modifiers, &md->modifier);
2281 md->node_group = get_node_group(object.id.lib);
2282 id_us_plus(&md->node_group->id);
2283
2284 md->settings.properties = idprop::create_group("Nodes Modifier Settings").release();
2285 IDProperty *angle_prop = idprop::create("Socket_2", angle).release();
2286 auto *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(IDP_ui_data_ensure(angle_prop));
2287 ui_data->base.rna_subtype = PROP_ANGLE;
2288 ui_data->soft_min = 0.0f;
2289 ui_data->soft_max = DEG2RADF(180.0f);
2290 IDP_AddToGroup(md->settings.properties, angle_prop);
2291 IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_use_attribute", 0).release());
2292 IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_attribute_name", "").release());
2293
2294 BKE_modifiers_persistent_uid_init(object, md->modifier);
2295 return &md->modifier;
2296}
2297
2298} // namespace blender::bke
2299
2301{
2302 using namespace blender;
2303 using namespace blender::bke;
2304
2305 /* Add the node group lazily and share it among all objects in the same library. */
2306 Map<Library *, bNodeTree *> group_by_library;
2307 const auto add_node_group = [&](Library *owner_library) {
2308 if (bNodeTree **group = group_by_library.lookup_ptr(owner_library)) {
2309 /* Node tree has already been found/created for this versioning call. */
2310 return *group;
2311 }
2312 /* Try to find an existing group added by previous versioning to avoid adding duplicates. */
2313 LISTBASE_FOREACH (bNodeTree *, existing_group, &bmain.nodetrees) {
2314 if (existing_group->id.lib != owner_library) {
2315 continue;
2316 }
2317 if (is_auto_smooth_node_tree(*existing_group)) {
2318 group_by_library.add_new(owner_library, existing_group);
2319 return existing_group;
2320 }
2321 }
2322 bNodeTree *new_group = add_auto_smooth_node_tree(bmain, owner_library);
2323 /* Remove the default user. The count is tracked manually when assigning to modifiers. */
2324 id_us_min(&new_group->id);
2325 group_by_library.add_new(owner_library, new_group);
2326 return new_group;
2327 };
2328
2329 LISTBASE_FOREACH (Object *, object, &bmain.objects) {
2330 if (object->type != OB_MESH) {
2331 continue;
2332 }
2333 Mesh *mesh = static_cast<Mesh *>(object->data);
2334 const float angle = mesh->smoothresh_legacy;
2335 if (!(mesh->flag & ME_AUTOSMOOTH_LEGACY)) {
2336 continue;
2337 }
2338
2339 /* Auto-smooth disabled sharp edge tagging when the evaluated mesh had custom normals.
2340 * When the original mesh has custom normals, that's a good sign the evaluated mesh will
2341 * have custom normals as well. */
2342 bool has_custom_normals = CustomData_has_layer(&mesh->corner_data, CD_CUSTOMLOOPNORMAL) ||
2344 &mesh->corner_data, CD_PROP_INT16_2D, "custom_normal");
2345 if (has_custom_normals) {
2346 continue;
2347 }
2348
2349 /* The "Weighted Normal" modifier has a "Keep Sharp" option that used to recalculate the sharp
2350 * edge tags based on the mesh's smoothing angle. To keep the same behavior, a new modifier has
2351 * to be added before that modifier when the option is on. */
2352 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
2354 has_custom_normals = true;
2355 }
2356 if (md->type == eModifierType_Bevel) {
2357 BevelModifierData *bmd = reinterpret_cast<BevelModifierData *>(md);
2358 if (bmd->flags & MOD_BEVEL_HARDEN_NORMALS) {
2359 has_custom_normals = true;
2360 }
2361 }
2362 if (md->type == eModifierType_WeightedNormal) {
2363 WeightedNormalModifierData *nmd = reinterpret_cast<WeightedNormalModifierData *>(md);
2364 if ((nmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0) {
2365 ModifierData *new_md = create_auto_smooth_modifier(*object, add_node_group, angle);
2366 BLI_insertlinkbefore(&object->modifiers, object->modifiers.last, new_md);
2367 }
2368 }
2369 if (md->type == eModifierType_Nodes) {
2370 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
2371 if (nmd->node_group && is_auto_smooth_node_tree(*nmd->node_group)) {
2372 /* This object has already been processed by versioning. If the mesh is linked from
2373 * another file its auto-smooth flag may not be cleared, so this check is necessary to
2374 * avoid adding a duplicate modifier. */
2375 has_custom_normals = true;
2376 break;
2377 }
2378 }
2379 }
2380
2381 /* Some modifiers always generate custom normals which disabled sharp edge tagging, making
2382 * adding a modifier at the end unnecessary. Conceptually this is similar to checking if the
2383 * evaluated mesh had custom normals. */
2384 if (has_custom_normals) {
2385 continue;
2386 }
2387
2388 ModifierData *last_md = static_cast<ModifierData *>(object->modifiers.last);
2389 ModifierData *new_md = create_auto_smooth_modifier(*object, add_node_group, angle);
2390 if (last_md && last_md->type == eModifierType_Subsurf && has_custom_normals &&
2391 (reinterpret_cast<SubsurfModifierData *>(last_md)->flags &
2393 {
2394 /* Add the auto smooth node group before the last subdivision surface modifier if possible.
2395 * Subdivision surface modifiers have special handling for interpolating custom normals. */
2396 BLI_insertlinkbefore(&object->modifiers, object->modifiers.last, new_md);
2397 }
2398 else {
2399 BLI_addtail(&object->modifiers, new_md);
2400 }
2401 }
2402
2403 LISTBASE_FOREACH (Mesh *, mesh, &bmain.meshes) {
2404 mesh->flag &= ~ME_AUTOSMOOTH_LEGACY;
2405 }
2406}
2407
2408namespace blender::bke {
2409
2411{
2412 if (mesh.attributes().contains(".sculpt_mask")) {
2413 return;
2414 }
2415 void *data = nullptr;
2416 const ImplicitSharingInfo *sharing_info = nullptr;
2417 for (const int i : IndexRange(mesh.vert_data.totlayer)) {
2418 CustomDataLayer &layer = mesh.vert_data.layers[i];
2419 if (layer.type == CD_PAINT_MASK) {
2420 data = layer.data;
2421 sharing_info = layer.sharing_info;
2422 layer.data = nullptr;
2423 layer.sharing_info = nullptr;
2424 CustomData_free_layer(&mesh.vert_data, CD_PAINT_MASK, i);
2425 break;
2426 }
2427 }
2428 if (data != nullptr) {
2430 &mesh.vert_data, CD_PROP_FLOAT, data, mesh.verts_num, ".sculpt_mask", sharing_info);
2431 }
2432 if (sharing_info != nullptr) {
2433 sharing_info->remove_user_and_delete_if_last();
2434 }
2435}
2436
2438{
2439 {
2440 void *data = nullptr;
2441 const ImplicitSharingInfo *sharing_info = nullptr;
2442 for (const int i : IndexRange(mesh.edge_data.totlayer)) {
2443 CustomDataLayer &layer = mesh.edge_data.layers[i];
2444 if (layer.type == CD_FREESTYLE_EDGE) {
2445 data = layer.data;
2446 sharing_info = layer.sharing_info;
2447 layer.data = nullptr;
2448 layer.sharing_info = nullptr;
2449 CustomData_free_layer(&mesh.edge_data, CD_FREESTYLE_EDGE, i);
2450 break;
2451 }
2452 }
2453 if (data != nullptr) {
2454 static_assert(sizeof(FreestyleEdge) == sizeof(bool));
2455 static_assert(char(FREESTYLE_EDGE_MARK) == char(true));
2457 &mesh.edge_data, CD_PROP_BOOL, data, mesh.edges_num, "freestyle_edge", sharing_info);
2458 }
2459 if (sharing_info != nullptr) {
2460 sharing_info->remove_user_and_delete_if_last();
2461 }
2462 }
2463 {
2464 void *data = nullptr;
2465 const ImplicitSharingInfo *sharing_info = nullptr;
2466 for (const int i : IndexRange(mesh.face_data.totlayer)) {
2467 CustomDataLayer &layer = mesh.face_data.layers[i];
2468 if (layer.type == CD_FREESTYLE_FACE) {
2469 data = layer.data;
2470 sharing_info = layer.sharing_info;
2471 layer.data = nullptr;
2472 layer.sharing_info = nullptr;
2473 CustomData_free_layer(&mesh.face_data, CD_FREESTYLE_FACE, i);
2474 break;
2475 }
2476 }
2477 if (data != nullptr) {
2478 static_assert(sizeof(FreestyleFace) == sizeof(bool));
2479 static_assert(char(FREESTYLE_FACE_MARK) == char(true));
2481 &mesh.face_data, CD_PROP_BOOL, data, mesh.faces_num, "freestyle_face", sharing_info);
2482 }
2483 if (sharing_info != nullptr) {
2484 sharing_info->remove_user_and_delete_if_last();
2485 }
2486 }
2487}
2488
2490 CustomData &edge_data,
2491 CustomData &face_data,
2492 Vector<CustomDataLayer, 16> &edge_layers,
2493 Vector<CustomDataLayer, 16> &face_layers)
2494{
2495 Array<bool, 64> attrs_to_remove(attr_write_data.attributes.size(), false);
2496 for (const int i : attr_write_data.attributes.index_range()) {
2497 const ::Attribute &dna_attr = attr_write_data.attributes[i];
2498 if (dna_attr.data_type != int8_t(AttrType::Bool)) {
2499 continue;
2500 }
2501 if (dna_attr.storage_type != int8_t(AttrStorageType::Array)) {
2502 continue;
2503 }
2504 if (dna_attr.domain == int8_t(AttrDomain::Edge)) {
2505 if (STREQ(dna_attr.name, "freestyle_edge")) {
2506 const auto &array_dna = *static_cast<const ::AttributeArray *>(dna_attr.data);
2507 static_assert(sizeof(FreestyleEdge) == sizeof(bool));
2508 static_assert(char(FREESTYLE_EDGE_MARK) == char(true));
2509 CustomDataLayer layer{};
2510 layer.type = CD_FREESTYLE_EDGE;
2511 layer.data = array_dna.data;
2512 layer.sharing_info = array_dna.sharing_info;
2513 edge_layers.append(layer);
2514 std::stable_sort(
2515 edge_layers.begin(),
2516 edge_layers.end(),
2517 [](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
2518 if (!edge_data.layers) {
2519 /* edge_data.layers must not be null, or the layers will not be written. Its address
2520 * doesn't really matter, but it must be unique within this ID.*/
2521 edge_data.layers = edge_layers.data();
2522 }
2523 edge_data.totlayer = edge_layers.size();
2524 edge_data.maxlayer = edge_data.totlayer;
2525 attrs_to_remove[i] = true;
2526 }
2527 }
2528 else if (dna_attr.domain == int8_t(AttrDomain::Face)) {
2529 if (STREQ(dna_attr.name, "freestyle_face")) {
2530 const auto &array_dna = *static_cast<const ::AttributeArray *>(dna_attr.data);
2531 static_assert(sizeof(FreestyleFace) == sizeof(bool));
2532 static_assert(char(FREESTYLE_FACE_MARK) == char(true));
2533 CustomDataLayer layer{};
2534 layer.type = CD_FREESTYLE_FACE;
2535 layer.data = array_dna.data;
2536 layer.sharing_info = array_dna.sharing_info;
2537 face_layers.append(layer);
2538 std::stable_sort(
2539 face_layers.begin(),
2540 face_layers.end(),
2541 [](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
2542 if (!face_data.layers) {
2543 /* face_data.layers must not be null, or the layers will not be written. Its address
2544 * doesn't really matter, but it must be unique within this ID.*/
2545 face_data.layers = face_layers.data();
2546 }
2547 face_data.totlayer = face_layers.size();
2548 face_data.maxlayer = face_data.totlayer;
2549 attrs_to_remove[i] = true;
2550 }
2551 }
2552 }
2553 attr_write_data.attributes.remove_if([&](const ::Attribute &attr) {
2554 const int i = &attr - attr_write_data.attributes.begin();
2555 return attrs_to_remove[i];
2556 });
2557}
2558
2560{
2561 if (mesh.attributes().contains("custom_normal")) {
2562 return;
2563 }
2564 void *data = nullptr;
2565 const ImplicitSharingInfo *sharing_info = nullptr;
2566 for (const int i : IndexRange(mesh.corner_data.totlayer)) {
2567 CustomDataLayer &layer = mesh.corner_data.layers[i];
2568 if (layer.type == CD_CUSTOMLOOPNORMAL) {
2569 data = layer.data;
2570 sharing_info = layer.sharing_info;
2571 layer.data = nullptr;
2572 layer.sharing_info = nullptr;
2573 CustomData_free_layer(&mesh.corner_data, CD_CUSTOMLOOPNORMAL, i);
2574 break;
2575 }
2576 }
2577 if (data != nullptr) {
2580 data,
2581 mesh.corners_num,
2582 "custom_normal",
2583 sharing_info);
2584 }
2585 if (sharing_info != nullptr) {
2586 sharing_info->remove_user_and_delete_if_last();
2587 }
2588}
2589
2591{
2592 const char *name = CustomData_get_active_layer_name(&mesh.corner_data, CD_PROP_FLOAT2);
2593 if (!name) {
2594 return;
2595 }
2596 const std::string uv_select_vert_name_shared = ".uv_select_vert";
2597 const std::string uv_select_edge_name_shared = ".uv_select_edge";
2598 const std::string uv_select_face_name_shared = ".uv_select_face";
2599
2600 const std::string uv_select_vert_prefix = ".vs.";
2601 const std::string uv_select_edge_prefix = ".es.";
2602
2603 const std::string uv_select_vert_name = uv_select_vert_prefix + name;
2604 const std::string uv_select_edge_name = uv_select_edge_prefix + name;
2605
2606 const int uv_select_vert = CustomData_get_named_layer_index(
2607 &mesh.corner_data, CD_PROP_BOOL, uv_select_vert_name);
2608 const int uv_select_edge = CustomData_get_named_layer_index(
2609 &mesh.corner_data, CD_PROP_BOOL, uv_select_edge_name);
2610
2611 if (uv_select_vert != -1 && uv_select_edge != -1) {
2612 /* Unlikely either exist but ensure there are no duplicate names. */
2613 CustomData_free_layer_named(&mesh.corner_data, uv_select_vert_name_shared);
2614 CustomData_free_layer_named(&mesh.corner_data, uv_select_edge_name_shared);
2615 CustomData_free_layer_named(&mesh.face_data, uv_select_face_name_shared);
2616
2617 STRNCPY_UTF8(mesh.corner_data.layers[uv_select_vert].name, uv_select_vert_name_shared.c_str());
2618 STRNCPY_UTF8(mesh.corner_data.layers[uv_select_edge].name, uv_select_edge_name_shared.c_str());
2619
2620 bool *uv_select_face = MEM_malloc_arrayN<bool>(mesh.faces_num, __func__);
2623 uv_select_face,
2624 mesh.faces_num,
2625 uv_select_face_name_shared,
2626 nullptr);
2627
2628 /* Create a face selection layer (flush from edges). */
2629 if (mesh.faces_num > 0) {
2630 const OffsetIndices<int> faces = mesh.faces();
2631 const Span<bool> uv_select_edge_data(
2632 static_cast<bool *>(mesh.corner_data.layers[uv_select_edge].data), mesh.corners_num);
2633 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
2634 for (const int face : range) {
2635 uv_select_face[face] = !uv_select_edge_data.slice(faces[face]).contains(false);
2636 }
2637 });
2638 }
2639 }
2640
2641 /* Logically a set as names are expected to be unique.
2642 * If there are duplicates, this will remove those too. */
2643 Vector<std::string> attributes_to_remove;
2644 for (const int i : IndexRange(mesh.corner_data.totlayer)) {
2645 const CustomDataLayer &layer = mesh.corner_data.layers[i];
2646 if (layer.type != CD_PROP_BOOL) {
2647 continue;
2648 }
2649 StringRef layer_name = StringRef(layer.name);
2650 if (layer_name.startswith(uv_select_vert_prefix) ||
2651 layer_name.startswith(uv_select_edge_prefix))
2652 {
2653 attributes_to_remove.append(layer.name);
2654 }
2655 }
2656
2657 for (const StringRef name_to_remove : attributes_to_remove) {
2658 CustomData_free_layer_named(&mesh.corner_data, name_to_remove);
2659 }
2660}
2661
2662} // namespace blender::bke
2663
2665
2667{
2668 const int nulegacy_faces = mesh->totface_legacy;
2670 eh.reserve(nulegacy_faces);
2671 MFace *legacy_faces = (MFace *)CustomData_get_layer_for_write(
2672 &mesh->fdata_legacy, CD_MFACE, mesh->totface_legacy);
2673
2674 MFace *mf = legacy_faces;
2675 for (int i = 0; i < nulegacy_faces; i++, mf++) {
2676 eh.add({mf->v1, mf->v2});
2677 eh.add({mf->v2, mf->v3});
2678
2679 if (mf->v4) {
2680 eh.add({mf->v3, mf->v4});
2681 eh.add({mf->v4, mf->v1});
2682 }
2683 else {
2684 eh.add({mf->v3, mf->v1});
2685 }
2686 }
2687
2688 const int numEdges = eh.size();
2689
2690 /* write new edges into a temporary CustomData */
2691 CustomData edgeData;
2692 CustomData_reset(&edgeData);
2693 CustomData_add_layer_named(&edgeData, CD_PROP_INT32_2D, CD_CONSTRUCT, numEdges, ".edge_verts");
2694 CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_SET_DEFAULT, numEdges);
2695
2697 &edgeData, CD_PROP_INT32_2D, ".edge_verts", mesh->edges_num);
2698 int *index = (int *)CustomData_get_layer_for_write(&edgeData, CD_ORIGINDEX, mesh->edges_num);
2699
2700 memset(index, ORIGINDEX_NONE, sizeof(int) * numEdges);
2701 MutableSpan(ege, numEdges).copy_from(eh.as_span().cast<blender::int2>());
2702
2703 /* free old CustomData and assign new one */
2704 CustomData_free(&mesh->edge_data);
2705 mesh->edge_data = edgeData;
2706 mesh->edges_num = numEdges;
2707}
blender::StringRef BKE_uv_map_pin_name_get(blender::StringRef uv_map_name, char *buffer)
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, blender::StringRef name)
Definition attribute.cc:370
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)
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)
bool CustomData_free_layer(CustomData *data, eCustomDataType type, int index)
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)
void CustomData_free(CustomData *data)
void CustomData_set_layer_clone(CustomData *data, eCustomDataType type, int n)
void CustomData_free_layers(CustomData *data, eCustomDataType type)
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_free_layer_named(CustomData *data, blender::StringRef name)
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:717
IDPropertyUIData * IDP_ui_data_ensure(IDProperty *prop)
Definition idprop.cc:1761
void id_us_plus(ID *id)
Definition lib_id.cc:358
void id_us_min(ID *id)
Definition lib_id.cc:366
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:693
void BKE_ntree_update_after_single_tree_change(Main &bmain, bNodeTree &modified_tree, const NodeTreeUpdateExtraParams &params={})
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
#define DEG2RADF(_deg)
#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])
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])
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
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.cc:41
#define STRNCPY_UTF8(dst, src)
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_TESSLOOPNORMAL
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_COLOR
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_ORIGSPACE_MLOOP
@ CD_PROP_INT16_2D
#define CD_MASK_MDISPS
@ ME_AUTOSMOOTH_LEGACY
struct MCol MCol
@ MOD_BEVEL_HARDEN_NORMALS
@ eModifierType_Subsurf
@ eModifierType_NormalEdit
@ eModifierType_Nodes
@ eModifierType_Bevel
@ eModifierType_WeightedNormal
@ MOD_WEIGHTEDNORMAL_KEEP_SHARP
@ eSubsurfModifierFlag_UseCustomNormals
@ GEO_NODE_ASSET_MODIFIER
@ NTREE_GEOMETRY
@ 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
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
@ PROP_ANGLE
Definition RNA_types.hh:252
BMesh const char void * data
return true
ATTR_WARN_UNUSED_RESULT const BMVert * v2
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
Span< T > as_span() const
Definition BLI_array.hh:243
const T * end() const
Definition BLI_array.hh:325
IndexRange index_range() const
Definition BLI_array.hh:360
const T * begin() const
Definition BLI_array.hh:321
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
void remove_user_and_delete_if_last() const
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
MapType::ItemIterator items() const
void add(const Key &key, const Value &value)
constexpr IndexRange drop_back(int64_t n) const
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
void reserve(int64_t n)
Definition BLI_map.hh:1028
constexpr T * end() const
Definition BLI_span.hh:548
constexpr T * begin() const
Definition BLI_span.hh:544
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr bool startswith(StringRef prefix) const
bool add(const Key &key)
void reserve(const int64_t n)
int64_t size() const
Span< Key > as_span() const
int64_t size() const
void append(const T &value)
bool contains(StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
nullptr float
#define logf(x)
static float verts[][3]
#define printf(...)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
#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 convert_mfaces_to_mpolys(ID *id, CustomData *fdata_legacy, CustomData *ldata, CustomData *pdata, int totedge_i, int totface_i, int, int, blender::int2 *edges, MFace *mface, int *r_totloop, int *r_faces_num)
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)
void BKE_mesh_legacy_attribute_flags_to_strings(Mesh *mesh)
void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh)
void fill_index_range(MutableSpan< T > span, const T start=0)
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRef 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(StringRef 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)
void mesh_freestyle_marks_to_generic(Mesh &mesh)
void mesh_freestyle_marks_to_legacy(AttributeStorage::BlendWriteData &attr_write_data, CustomData &edge_data, CustomData &face_data, Vector< CustomDataLayer, 16 > &edge_layers, Vector< CustomDataLayer, 16 > &face_layers)
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2532
bNode * node_add_node(const bContext *C, bNodeTree &ntree, StringRef idname, std::optional< int > unique_identifier=std::nullopt)
Definition node.cc:3477
static bool is_auto_smooth_node_tree(const bNodeTree &group)
bool node_set_selected(bNode &node, bool select)
Definition node.cc:4695
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:3810
void mesh_custom_normals_to_generic(Mesh &mesh)
void mesh_uv_select_to_single_attribute(Mesh &mesh)
bNodeTree * node_tree_add_in_lib(Main *bmain, Library *owner_library, StringRefNull name, StringRefNull idname)
Definition node.cc:4090
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)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
static blender::bke::bNodeSocketTemplate inputs[]
static void init(bNodeTree *, bNode *node)
#define hash
Definition noise_c.cc:154
const char * name
#define sqrtf
const ImplicitSharingInfoHandle * sharing_info
CustomDataLayer * layers
CustomDataExternal * external
IDPropertyUIData base
Definition DNA_ID.h:106
Definition DNA_ID.h:414
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:281
ListBase nodetrees
Definition BKE_main.hh:301
ListBase objects
Definition BKE_main.hh:280
int corners_num
CustomData edge_data
int edges_num
CustomData corner_data
CustomData face_data
char * default_color_attribute
CustomData vert_data
CustomData fdata_legacy
int totface_legacy
int faces_num
int verts_num
char * active_color_attribute
struct bNodeTree * node_group
struct GeometryNodeAssetTraits * geometry_node_asset_traits
bNodeTreeInterface tree_interface
ListBase nodes
ListBase links
float location[2]
int16_t custom1
void * storage
ListBase outputs
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
static DynamicLibrary lib