diff options
Diffstat (limited to 'source/blender/blenkernel/intern/mesh_tessellate.c')
-rw-r--r-- | source/blender/blenkernel/intern/mesh_tessellate.c | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c new file mode 100644 index 00000000000..213f2929d63 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -0,0 +1,760 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + * + * This file contains code for polygon tessellation + * (creating triangles from polygons). + * + * \see bmesh_mesh_tessellate.c for the #BMesh equivalent of this file. + */ + +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" /* Own include. */ + +#include "BLI_strict_flags.h" + +/** Compared against total loops. */ +#define MESH_FACE_TESSELLATE_THREADED_LIMIT 4096 + +/* -------------------------------------------------------------------- */ +/** \name MFace Tessellation + * + * #MFace is a legacy data-structure that should be avoided, use #MLoopTri instead. + * \{ */ + +/** + * Convert all CD layers from loop/poly to tessface data. + * + * \param loopindices: is an array of an int[4] per tessface, + * mapping tessface's verts to loops indices. + * + * \note when mface is not NULL, mface[face_index].v4 + * is used to test quads, else, loopindices[face_index][3] is used. + */ +static void mesh_loops_to_tessdata(CustomData *fdata, + CustomData *ldata, + MFace *mface, + const int *polyindices, + uint (*loopindices)[4], + const int num_faces) +{ + /* NOTE(mont29): performances are sub-optimal when we get a NULL #MFace, + * we could be ~25% quicker with dedicated code. + * The issue is, unless having two different functions with nearly the same code, + * there's not much ways to solve this. Better IMHO to live with it for now (sigh). */ + const int numUV = CustomData_number_of_layers(ldata, CD_MLOOPUV); + const int numCol = CustomData_number_of_layers(ldata, CD_MLOOPCOL); + const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL); + const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP); + const bool hasLoopNormal = CustomData_has_layer(ldata, CD_NORMAL); + const bool hasLoopTangent = CustomData_has_layer(ldata, CD_TANGENT); + int findex, i, j; + const int *pidx; + uint(*lidx)[4]; + + for (i = 0; i < numUV; i++) { + MTFace *texface = CustomData_get_layer_n(fdata, CD_MTFACE, i); + MLoopUV *mloopuv = CustomData_get_layer_n(ldata, CD_MLOOPUV, i); + + for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; + pidx++, lidx++, findex++, texface++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + copy_v2_v2(texface->uv[j], mloopuv[(*lidx)[j]].uv); + } + } + } + + for (i = 0; i < numCol; i++) { + MCol(*mcol)[4] = CustomData_get_layer_n(fdata, CD_MCOL, i); + MLoopCol *mloopcol = CustomData_get_layer_n(ldata, CD_MLOOPCOL, i); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); + } + } + } + + if (hasPCol) { + MCol(*mcol)[4] = CustomData_get_layer(fdata, CD_PREVIEW_MCOL); + MLoopCol *mloopcol = CustomData_get_layer(ldata, CD_PREVIEW_MLOOPCOL); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]); + } + } + } + + if (hasOrigSpace) { + OrigSpaceFace *of = CustomData_get_layer(fdata, CD_ORIGSPACE); + OrigSpaceLoop *lof = CustomData_get_layer(ldata, CD_ORIGSPACE_MLOOP); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + copy_v2_v2(of->uv[j], lof[(*lidx)[j]].uv); + } + } + } + + if (hasLoopNormal) { + short(*fnors)[4][3] = CustomData_get_layer(fdata, CD_TESSLOOPNORMAL); + float(*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL); + + for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) { + for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) { + normal_float_to_short_v3((*fnors)[j], lnors[(*lidx)[j]]); + } + } + } + + if (hasLoopTangent) { + /* Need to do for all UV maps at some point. */ + float(*ftangents)[4] = CustomData_get_layer(fdata, CD_TANGENT); + float(*ltangents)[4] = CustomData_get_layer(ldata, CD_TANGENT); + + for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces; + pidx++, lidx++, findex++) { + int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; + for (j = nverts; j--;) { + copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]); + } + } + } +} + +/** + * Recreate #MFace Tessellation. + * + * \param do_face_nor_copy: Controls whether the normals from the poly + * are copied to the tessellated faces. + * + * \return number of tessellation faces. + * + * \note This doesn't use multi-threading like #BKE_mesh_recalc_looptri since + * it's not used in many places and #MFace should be phased out. + */ +int BKE_mesh_tessface_calc_ex(CustomData *fdata, + CustomData *ldata, + CustomData *pdata, + MVert *mvert, + int totface, + int totloop, + int totpoly, + const bool do_face_nor_copy) +{ +#define USE_TESSFACE_SPEEDUP +#define USE_TESSFACE_QUADS + +/* We abuse #MFace.edcode to tag quad faces. See below for details. */ +#define TESSFACE_IS_QUAD 1 + + const int looptri_num = poly_to_tri_count(totpoly, totloop); + + MPoly *mp, *mpoly; + MLoop *ml, *mloop; + MFace *mface, *mf; + MemArena *arena = NULL; + int *mface_to_poly_map; + uint(*lindices)[4]; + int poly_index, mface_index; + uint j; + + mpoly = CustomData_get_layer(pdata, CD_MPOLY); + mloop = CustomData_get_layer(ldata, CD_MLOOP); + + /* Allocate the length of `totfaces`, avoid many small reallocation's, + * if all faces are triangles it will be correct, `quads == 2x` allocations. */ + /* Take care since memory is _not_ zeroed so be sure to initialize each field. */ + mface_to_poly_map = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface_to_poly_map), __func__); + mface = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*mface), __func__); + lindices = MEM_malloc_arrayN((size_t)looptri_num, sizeof(*lindices), __func__); + + mface_index = 0; + mp = mpoly; + for (poly_index = 0; poly_index < totpoly; poly_index++, mp++) { + const uint mp_loopstart = (uint)mp->loopstart; + const uint mp_totloop = (uint)mp->totloop; + uint l1, l2, l3, l4; + uint *lidx; + if (mp_totloop < 3) { + /* Do nothing. */ + } + +#ifdef USE_TESSFACE_SPEEDUP + +# define ML_TO_MF(i1, i2, i3) \ + mface_to_poly_map[mface_index] = poly_index; \ + mf = &mface[mface_index]; \ + lidx = lindices[mface_index]; \ + /* Set loop indices, transformed to vert indices later. */ \ + l1 = mp_loopstart + i1; \ + l2 = mp_loopstart + i2; \ + l3 = mp_loopstart + i3; \ + mf->v1 = mloop[l1].v; \ + mf->v2 = mloop[l2].v; \ + mf->v3 = mloop[l3].v; \ + mf->v4 = 0; \ + lidx[0] = l1; \ + lidx[1] = l2; \ + lidx[2] = l3; \ + lidx[3] = 0; \ + mf->mat_nr = mp->mat_nr; \ + mf->flag = mp->flag; \ + mf->edcode = 0; \ + (void)0 + +/* ALMOST IDENTICAL TO DEFINE ABOVE (see EXCEPTION) */ +# define ML_TO_MF_QUAD() \ + mface_to_poly_map[mface_index] = poly_index; \ + mf = &mface[mface_index]; \ + lidx = lindices[mface_index]; \ + /* Set loop indices, transformed to vert indices later. */ \ + l1 = mp_loopstart + 0; /* EXCEPTION */ \ + l2 = mp_loopstart + 1; /* EXCEPTION */ \ + l3 = mp_loopstart + 2; /* EXCEPTION */ \ + l4 = mp_loopstart + 3; /* EXCEPTION */ \ + mf->v1 = mloop[l1].v; \ + mf->v2 = mloop[l2].v; \ + mf->v3 = mloop[l3].v; \ + mf->v4 = mloop[l4].v; \ + lidx[0] = l1; \ + lidx[1] = l2; \ + lidx[2] = l3; \ + lidx[3] = l4; \ + mf->mat_nr = mp->mat_nr; \ + mf->flag = mp->flag; \ + mf->edcode = TESSFACE_IS_QUAD; \ + (void)0 + + else if (mp_totloop == 3) { + ML_TO_MF(0, 1, 2); + mface_index++; + } + else if (mp_totloop == 4) { +# ifdef USE_TESSFACE_QUADS + ML_TO_MF_QUAD(); + mface_index++; +# else + ML_TO_MF(0, 1, 2); + mface_index++; + ML_TO_MF(0, 2, 3); + mface_index++; +# endif + } +#endif /* USE_TESSFACE_SPEEDUP */ + else { + const float *co_curr, *co_prev; + + float normal[3]; + + float axis_mat[3][3]; + float(*projverts)[2]; + uint(*tris)[3]; + + const uint totfilltri = mp_totloop - 2; + + if (UNLIKELY(arena == NULL)) { + arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + tris = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)totfilltri); + projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)mp_totloop); + + zero_v3(normal); + + /* Calculate the normal, flipped: to get a positive 2D cross product. */ + ml = mloop + mp_loopstart; + co_prev = mvert[ml[mp_totloop - 1].v].co; + for (j = 0; j < mp_totloop; j++, ml++) { + co_curr = mvert[ml->v].co; + add_newell_cross_v3_v3v3(normal, co_prev, co_curr); + co_prev = co_curr; + } + if (UNLIKELY(normalize_v3(normal) == 0.0f)) { + normal[2] = 1.0f; + } + + /* Project verts to 2D. */ + axis_dominant_v3_to_m3_negate(axis_mat, normal); + + ml = mloop + mp_loopstart; + for (j = 0; j < mp_totloop; j++, ml++) { + mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); + } + + BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena); + + /* Apply fill. */ + for (j = 0; j < totfilltri; j++) { + uint *tri = tris[j]; + lidx = lindices[mface_index]; + + mface_to_poly_map[mface_index] = poly_index; + mf = &mface[mface_index]; + + /* Set loop indices, transformed to vert indices later. */ + l1 = mp_loopstart + tri[0]; + l2 = mp_loopstart + tri[1]; + l3 = mp_loopstart + tri[2]; + + mf->v1 = mloop[l1].v; + mf->v2 = mloop[l2].v; + mf->v3 = mloop[l3].v; + mf->v4 = 0; + + lidx[0] = l1; + lidx[1] = l2; + lidx[2] = l3; + lidx[3] = 0; + + mf->mat_nr = mp->mat_nr; + mf->flag = mp->flag; + mf->edcode = 0; + + mface_index++; + } + + BLI_memarena_clear(arena); + } + } + + if (arena) { + BLI_memarena_free(arena); + arena = NULL; + } + + CustomData_free(fdata, totface); + totface = mface_index; + + BLI_assert(totface <= looptri_num); + + /* Not essential but without this we store over-allocated memory in the #CustomData layers. */ + if (LIKELY(looptri_num != totface)) { + mface = MEM_reallocN(mface, sizeof(*mface) * (size_t)totface); + mface_to_poly_map = MEM_reallocN(mface_to_poly_map, + sizeof(*mface_to_poly_map) * (size_t)totface); + } + + CustomData_add_layer(fdata, CD_MFACE, CD_ASSIGN, mface, totface); + + /* #CD_ORIGINDEX will contain an array of indices from tessellation-faces to the polygons + * they are directly tessellated from. */ + CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface); + CustomData_from_bmeshpoly(fdata, ldata, totface); + + if (do_face_nor_copy) { + /* If polys have a normals layer, copying that to faces can help + * avoid the need to recalculate normals later. */ + if (CustomData_has_layer(pdata, CD_NORMAL)) { + float(*pnors)[3] = CustomData_get_layer(pdata, CD_NORMAL); + float(*fnors)[3] = CustomData_add_layer(fdata, CD_NORMAL, CD_CALLOC, NULL, totface); + for (mface_index = 0; mface_index < totface; mface_index++) { + copy_v3_v3(fnors[mface_index], pnors[mface_to_poly_map[mface_index]]); + } + } + } + + /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: + * Polygons take care of their loops ordering, hence not of their vertices ordering. + * Currently, our tfaces' fourth vertex index might be 0 even for a quad. + * However, we know our fourth loop index is never 0 for quads + * (because they are sorted for polygons, and our quads are still mere copies of their polygons). + * So we pass NULL as MFace pointer, and #mesh_loops_to_tessdata + * will use the fourth loop index as quad test. */ + mesh_loops_to_tessdata(fdata, ldata, NULL, mface_to_poly_map, lindices, totface); + + /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: + * ...However, most TFace code uses 'MFace->v4 == 0' test to check whether it is a tri or quad. + * BKE_mesh_mface_index_validate() will check this and rotate the tessellated face if needed. + */ +#ifdef USE_TESSFACE_QUADS + mf = mface; + for (mface_index = 0; mface_index < totface; mface_index++, mf++) { + if (mf->edcode == TESSFACE_IS_QUAD) { + BKE_mesh_mface_index_validate(mf, fdata, mface_index, 4); + mf->edcode = 0; + } + } +#endif + + MEM_freeN(lindices); + + return totface; + +#undef USE_TESSFACE_SPEEDUP +#undef USE_TESSFACE_QUADS + +#undef ML_TO_MF +#undef ML_TO_MF_QUAD +} + +void BKE_mesh_tessface_calc(Mesh *mesh) +{ + mesh->totface = BKE_mesh_tessface_calc_ex( + &mesh->fdata, + &mesh->ldata, + &mesh->pdata, + mesh->mvert, + mesh->totface, + mesh->totloop, + mesh->totpoly, + /* Calculate normals right after, don't copy from polys here. */ + false); + + BKE_mesh_update_customdata_pointers(mesh, true); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop Tessellation + * + * Fill in #MLoopTri data-structure. + * \{ */ + +/** + * \param face_normal: This will be optimized out as a constant. + */ +BLI_INLINE void mesh_calc_tessellation_for_face_impl(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p, + const bool face_normal, + const float normal_precalc[3]) +{ + const uint mp_loopstart = (uint)mpoly[poly_index].loopstart; + const uint mp_totloop = (uint)mpoly[poly_index].totloop; + +#define ML_TO_MLT(i1, i2, i3) \ + { \ + ARRAY_SET_ITEMS(mlt->tri, mp_loopstart + i1, mp_loopstart + i2, mp_loopstart + i3); \ + mlt->poly = poly_index; \ + } \ + ((void)0) + + switch (mp_totloop) { + case 3: { + ML_TO_MLT(0, 1, 2); + break; + } + case 4: { + ML_TO_MLT(0, 1, 2); + MLoopTri *mlt_a = mlt++; + ML_TO_MLT(0, 2, 3); + MLoopTri *mlt_b = mlt; + + if (UNLIKELY(face_normal ? is_quad_flip_v3_first_third_fast_with_normal( + /* Simpler calculation (using the normal). */ + mvert[mloop[mlt_a->tri[0]].v].co, + mvert[mloop[mlt_a->tri[1]].v].co, + mvert[mloop[mlt_a->tri[2]].v].co, + mvert[mloop[mlt_b->tri[2]].v].co, + normal_precalc) : + is_quad_flip_v3_first_third_fast( + /* Expensive calculation (no normal). */ + mvert[mloop[mlt_a->tri[0]].v].co, + mvert[mloop[mlt_a->tri[1]].v].co, + mvert[mloop[mlt_a->tri[2]].v].co, + mvert[mloop[mlt_b->tri[2]].v].co))) { + /* Flip out of degenerate 0-2 state. */ + mlt_a->tri[2] = mlt_b->tri[2]; + mlt_b->tri[0] = mlt_a->tri[1]; + } + break; + } + default: { + const MLoop *ml; + float axis_mat[3][3]; + + /* Calculate `axis_mat` to project verts to 2D. */ + if (face_normal == false) { + float normal[3]; + const float *co_curr, *co_prev; + + zero_v3(normal); + + /* Calc normal, flipped: to get a positive 2D cross product. */ + ml = mloop + mp_loopstart; + co_prev = mvert[ml[mp_totloop - 1].v].co; + for (uint j = 0; j < mp_totloop; j++, ml++) { + co_curr = mvert[ml->v].co; + add_newell_cross_v3_v3v3(normal, co_prev, co_curr); + co_prev = co_curr; + } + if (UNLIKELY(normalize_v3(normal) == 0.0f)) { + normal[2] = 1.0f; + } + axis_dominant_v3_to_m3_negate(axis_mat, normal); + } + else { + axis_dominant_v3_to_m3_negate(axis_mat, normal_precalc); + } + + const uint totfilltri = mp_totloop - 2; + + MemArena *pf_arena = *pf_arena_p; + if (UNLIKELY(pf_arena == NULL)) { + pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + uint(*tris)[3] = tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * (size_t)totfilltri); + float(*projverts)[2] = projverts = BLI_memarena_alloc( + pf_arena, sizeof(*projverts) * (size_t)mp_totloop); + + ml = mloop + mp_loopstart; + for (uint j = 0; j < mp_totloop; j++, ml++) { + mul_v2_m3v3(projverts[j], axis_mat, mvert[ml->v].co); + } + + BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, pf_arena); + + /* Apply fill. */ + for (uint j = 0; j < totfilltri; j++, mlt++) { + const uint *tri = tris[j]; + ML_TO_MLT(tri[0], tri[1], tri[2]); + } + + BLI_memarena_clear(pf_arena); + + break; + } + } +#undef ML_TO_MLT +} + +static void mesh_calc_tessellation_for_face(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p) +{ + mesh_calc_tessellation_for_face_impl( + mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, NULL); +} + +static void mesh_calc_tessellation_for_face_with_normal(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + uint poly_index, + MLoopTri *mlt, + MemArena **pf_arena_p, + const float normal_precalc[3]) +{ + mesh_calc_tessellation_for_face_impl( + mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, true, normal_precalc); +} + +static void mesh_recalc_looptri__single_threaded(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + MemArena *pf_arena = NULL; + const MPoly *mp = mpoly; + uint tri_index = 0; + + if (poly_normals != NULL) { + for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { + mesh_calc_tessellation_for_face_with_normal(mloop, + mpoly, + mvert, + poly_index, + &mlooptri[tri_index], + &pf_arena, + poly_normals[poly_index]); + tri_index += (uint)(mp->totloop - 2); + } + } + else { + for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { + mesh_calc_tessellation_for_face( + mloop, mpoly, mvert, poly_index, &mlooptri[tri_index], &pf_arena); + tri_index += (uint)(mp->totloop - 2); + } + } + + if (pf_arena) { + BLI_memarena_free(pf_arena); + pf_arena = NULL; + } + BLI_assert(tri_index == (uint)poly_to_tri_count(totpoly, totloop)); + UNUSED_VARS_NDEBUG(totloop); +} + +struct TessellationUserData { + const MLoop *mloop; + const MPoly *mpoly; + const MVert *mvert; + + /** Output array. */ + MLoopTri *mlooptri; + + /** Optional pre-calculated polygon normals array. */ + const float (*poly_normals)[3]; +}; + +struct TessellationUserTLS { + MemArena *pf_arena; +}; + +static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + const struct TessellationUserData *data = userdata; + struct TessellationUserTLS *tls_data = tls->userdata_chunk; + const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); + mesh_calc_tessellation_for_face_impl(data->mloop, + data->mpoly, + data->mvert, + (uint)index, + &data->mlooptri[tri_index], + &tls_data->pf_arena, + false, + NULL); +} + +static void mesh_calc_tessellation_for_face_with_normal_fn(void *__restrict userdata, + const int index, + const TaskParallelTLS *__restrict tls) +{ + const struct TessellationUserData *data = userdata; + struct TessellationUserTLS *tls_data = tls->userdata_chunk; + const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); + mesh_calc_tessellation_for_face_impl(data->mloop, + data->mpoly, + data->mvert, + (uint)index, + &data->mlooptri[tri_index], + &tls_data->pf_arena, + true, + data->poly_normals[index]); +} + +static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata), + void *__restrict tls_v) +{ + struct TessellationUserTLS *tls_data = tls_v; + if (tls_data->pf_arena) { + BLI_memarena_free(tls_data->pf_arena); + } +} + +static void mesh_recalc_looptri__multi_threaded(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int UNUSED(totloop), + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + struct TessellationUserTLS tls_data_dummy = {NULL}; + + struct TessellationUserData data = { + .mloop = mloop, + .mpoly = mpoly, + .mvert = mvert, + .mlooptri = mlooptri, + .poly_normals = poly_normals, + }; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + + settings.userdata_chunk = &tls_data_dummy; + settings.userdata_chunk_size = sizeof(tls_data_dummy); + + settings.func_free = mesh_calc_tessellation_for_face_free_fn; + + BLI_task_parallel_range(0, + totpoly, + &data, + poly_normals ? mesh_calc_tessellation_for_face_with_normal_fn : + mesh_calc_tessellation_for_face_fn, + &settings); +} + +/** + * Calculate tessellation into #MLoopTri which exist only for this purpose. + */ +void BKE_mesh_recalc_looptri(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri) +{ + if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { + mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); + } + else { + mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); + } +} + +/** + * A version of #BKE_mesh_recalc_looptri which takes pre-calculated polygon normals + * (used to avoid having to calculate the face normal for NGON tessellation). + * + * \note Only use this function if normals have already been calculated, there is no need + * to calculate normals just to use this function as it will cause the normals for triangles + * to be calculated which aren't needed for tessellation. + */ +void BKE_mesh_recalc_looptri_with_normals(const MLoop *mloop, + const MPoly *mpoly, + const MVert *mvert, + int totloop, + int totpoly, + MLoopTri *mlooptri, + const float (*poly_normals)[3]) +{ + BLI_assert(poly_normals != NULL); + if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { + mesh_recalc_looptri__single_threaded( + mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals); + } + else { + mesh_recalc_looptri__multi_threaded( + mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals); + } +} + +/** \} */ |