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