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