diff options
Diffstat (limited to 'source/blender/blenkernel/intern/mesh_tangent.cc')
-rw-r--r-- | source/blender/blenkernel/intern/mesh_tangent.cc | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc new file mode 100644 index 00000000000..4ebc6b74705 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -0,0 +1,746 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + * + * Functions to evaluate mesh tangents. + */ + +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_tangent.h" +#include "BKE_report.h" + +#include "BLI_strict_flags.h" + +#include "atomic_ops.h" +#include "mikktspace.h" + +/* -------------------------------------------------------------------- */ +/** \name Mesh Tangent Calculations (Single Layer) + * \{ */ + +/* Tangent space utils. */ + +/* User data. */ +struct BKEMeshToTangent { + const MPoly *mpolys; /* faces */ + const MLoop *mloops; /* faces's vertices */ + const MVert *mverts; /* vertices */ + const MLoopUV *luvs; /* texture coordinates */ + const float (*lnors)[3]; /* loops' normals */ + float (*tangents)[4]; /* output tangents */ + int num_polys; /* number of polygons */ +}; + +/* Mikktspace's API */ +static int get_num_faces(const SMikkTSpaceContext *pContext) +{ + BKEMeshToTangent *p_mesh = static_cast<BKEMeshToTangent *>(pContext->m_pUserData); + return p_mesh->num_polys; +} + +static int get_num_verts_of_face(const SMikkTSpaceContext *pContext, const int face_idx) +{ + BKEMeshToTangent *p_mesh = static_cast<BKEMeshToTangent *>(pContext->m_pUserData); + return p_mesh->mpolys[face_idx].totloop; +} + +static void get_position(const SMikkTSpaceContext *pContext, + float r_co[3], + const int face_idx, + const int vert_idx) +{ + BKEMeshToTangent *p_mesh = static_cast<BKEMeshToTangent *>(pContext->m_pUserData); + const int loop_idx = p_mesh->mpolys[face_idx].loopstart + vert_idx; + copy_v3_v3(r_co, p_mesh->mverts[p_mesh->mloops[loop_idx].v].co); +} + +static void get_texture_coordinate(const SMikkTSpaceContext *pContext, + float r_uv[2], + const int face_idx, + const int vert_idx) +{ + BKEMeshToTangent *p_mesh = static_cast<BKEMeshToTangent *>(pContext->m_pUserData); + copy_v2_v2(r_uv, p_mesh->luvs[p_mesh->mpolys[face_idx].loopstart + vert_idx].uv); +} + +static void get_normal(const SMikkTSpaceContext *pContext, + float r_no[3], + const int face_idx, + const int vert_idx) +{ + BKEMeshToTangent *p_mesh = static_cast<BKEMeshToTangent *>(pContext->m_pUserData); + copy_v3_v3(r_no, p_mesh->lnors[p_mesh->mpolys[face_idx].loopstart + vert_idx]); +} + +static void set_tspace(const SMikkTSpaceContext *pContext, + const float fv_tangent[3], + const float face_sign, + const int face_idx, + const int vert_idx) +{ + BKEMeshToTangent *p_mesh = static_cast<BKEMeshToTangent *>(pContext->m_pUserData); + float *p_res = p_mesh->tangents[p_mesh->mpolys[face_idx].loopstart + vert_idx]; + copy_v3_v3(p_res, fv_tangent); + p_res[3] = face_sign; +} + +void BKE_mesh_calc_loop_tangent_single_ex(const MVert *mverts, + const int UNUSED(numVerts), + const MLoop *mloops, + float (*r_looptangent)[4], + const float (*loopnors)[3], + const MLoopUV *loopuvs, + const int UNUSED(numLoops), + const MPoly *mpolys, + const int numPolys, + ReportList *reports) +{ + BKEMeshToTangent mesh_to_tangent; + SMikkTSpaceContext s_context{}; + SMikkTSpaceInterface s_interface{}; + + const MPoly *mp; + int mp_index; + + /* First check we do have a tris/quads only mesh. */ + for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { + if (mp->totloop > 4) { + BKE_report( + reports, RPT_ERROR, "Tangent space can only be computed for tris/quads, aborting"); + return; + } + } + + /* Compute Mikktspace's tangent normals. */ + mesh_to_tangent.mpolys = mpolys; + mesh_to_tangent.mloops = mloops; + mesh_to_tangent.mverts = mverts; + mesh_to_tangent.luvs = loopuvs; + mesh_to_tangent.lnors = loopnors; + mesh_to_tangent.tangents = r_looptangent; + mesh_to_tangent.num_polys = numPolys; + + s_context.m_pUserData = &mesh_to_tangent; + s_context.m_pInterface = &s_interface; + s_interface.m_getNumFaces = get_num_faces; + s_interface.m_getNumVerticesOfFace = get_num_verts_of_face; + s_interface.m_getPosition = get_position; + s_interface.m_getTexCoord = get_texture_coordinate; + s_interface.m_getNormal = get_normal; + s_interface.m_setTSpaceBasic = set_tspace; + s_interface.m_setTSpace = nullptr; + + /* 0 if failed */ + if (genTangSpaceDefault(&s_context) == false) { + BKE_report(reports, RPT_ERROR, "Mikktspace failed to generate tangents for this mesh!"); + } +} + +void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, + const char *uvmap, + float (*r_looptangents)[4], + ReportList *reports) +{ + const MLoopUV *loopuvs; + + /* Check we have valid texture coordinates first! */ + if (uvmap) { + loopuvs = static_cast<MLoopUV *>(CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvmap)); + } + else { + loopuvs = static_cast<MLoopUV *>(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV)); + } + if (!loopuvs) { + BKE_reportf(reports, + RPT_ERROR, + "Tangent space computation needs a UV Map, \"%s\" not found, aborting", + uvmap); + return; + } + + const float(*loopnors)[3] = static_cast<const float(*)[3]>( + CustomData_get_layer(&mesh->ldata, CD_NORMAL)); + if (!loopnors) { + BKE_report( + reports, RPT_ERROR, "Tangent space computation needs loop normals, none found, aborting"); + return; + } + + BKE_mesh_calc_loop_tangent_single_ex(BKE_mesh_vertices(mesh), + mesh->totvert, + BKE_mesh_loops(mesh), + r_looptangents, + loopnors, + loopuvs, + mesh->totloop, + BKE_mesh_polygons(mesh), + mesh->totpoly, + reports); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mesh Tangent Calculations (All Layers) + * \{ */ + +/* Necessary complexity to handle looptri's as quads for correct tangents */ +#define USE_LOOPTRI_DETECT_QUADS + +struct SGLSLMeshToTangent { + const float (*precomputedFaceNormals)[3]; + const float (*precomputedLoopNormals)[3]; + const MLoopTri *looptri; + const MLoopUV *mloopuv; /* texture coordinates */ + const MPoly *mpoly; /* indices */ + const MLoop *mloop; /* indices */ + const MVert *mvert; /* vertex coordinates */ + const float (*vert_normals)[3]; + const float (*orco)[3]; + float (*tangent)[4]; /* destination */ + int numTessFaces; + +#ifdef USE_LOOPTRI_DETECT_QUADS + /* map from 'fake' face index to looptri, + * quads will point to the first looptri of the quad */ + const int *face_as_quad_map; + int num_face_as_quad_map; +#endif +}; + +/* interface */ +static int dm_ts_GetNumFaces(const SMikkTSpaceContext *pContext) +{ + SGLSLMeshToTangent *pMesh = static_cast<SGLSLMeshToTangent *>(pContext->m_pUserData); + +#ifdef USE_LOOPTRI_DETECT_QUADS + return pMesh->num_face_as_quad_map; +#else + return pMesh->numTessFaces; +#endif +} + +static int dm_ts_GetNumVertsOfFace(const SMikkTSpaceContext *pContext, const int face_num) +{ +#ifdef USE_LOOPTRI_DETECT_QUADS + SGLSLMeshToTangent *pMesh = static_cast<SGLSLMeshToTangent *>(pContext->m_pUserData); + if (pMesh->face_as_quad_map) { + const MLoopTri *lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]]; + const MPoly *mp = &pMesh->mpoly[lt->poly]; + if (mp->totloop == 4) { + return 4; + } + } + return 3; +#else + UNUSED_VARS(pContext, face_num); + return 3; +#endif +} + +static void dm_ts_GetPosition(const SMikkTSpaceContext *pContext, + float r_co[3], + const int face_num, + const int vert_index) +{ + // assert(vert_index >= 0 && vert_index < 4); + SGLSLMeshToTangent *pMesh = static_cast<SGLSLMeshToTangent *>(pContext->m_pUserData); + const MLoopTri *lt; + uint loop_index; + const float *co; + +#ifdef USE_LOOPTRI_DETECT_QUADS + if (pMesh->face_as_quad_map) { + lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]]; + const MPoly *mp = &pMesh->mpoly[lt->poly]; + if (mp->totloop == 4) { + loop_index = (uint)(mp->loopstart + vert_index); + goto finally; + } + /* fall through to regular triangle */ + } + else { + lt = &pMesh->looptri[face_num]; + } +#else + lt = &pMesh->looptri[face_num]; +#endif + loop_index = lt->tri[vert_index]; + +finally: + co = pMesh->mvert[pMesh->mloop[loop_index].v].co; + copy_v3_v3(r_co, co); +} + +static void dm_ts_GetTextureCoordinate(const SMikkTSpaceContext *pContext, + float r_uv[2], + const int face_num, + const int vert_index) +{ + // assert(vert_index >= 0 && vert_index < 4); + SGLSLMeshToTangent *pMesh = static_cast<SGLSLMeshToTangent *>(pContext->m_pUserData); + const MLoopTri *lt; + uint loop_index; + +#ifdef USE_LOOPTRI_DETECT_QUADS + if (pMesh->face_as_quad_map) { + lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]]; + const MPoly *mp = &pMesh->mpoly[lt->poly]; + if (mp->totloop == 4) { + loop_index = (uint)(mp->loopstart + vert_index); + goto finally; + } + /* fall through to regular triangle */ + } + else { + lt = &pMesh->looptri[face_num]; + } +#else + lt = &pMesh->looptri[face_num]; +#endif + loop_index = lt->tri[vert_index]; + +finally: + if (pMesh->mloopuv != nullptr) { + const float *uv = pMesh->mloopuv[loop_index].uv; + copy_v2_v2(r_uv, uv); + } + else { + const float *orco = pMesh->orco[pMesh->mloop[loop_index].v]; + map_to_sphere(&r_uv[0], &r_uv[1], orco[0], orco[1], orco[2]); + } +} + +static void dm_ts_GetNormal(const SMikkTSpaceContext *pContext, + float r_no[3], + const int face_num, + const int vert_index) +{ + // assert(vert_index >= 0 && vert_index < 4); + SGLSLMeshToTangent *pMesh = static_cast<SGLSLMeshToTangent *>(pContext->m_pUserData); + const MLoopTri *lt; + uint loop_index; + +#ifdef USE_LOOPTRI_DETECT_QUADS + if (pMesh->face_as_quad_map) { + lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]]; + const MPoly *mp = &pMesh->mpoly[lt->poly]; + if (mp->totloop == 4) { + loop_index = (uint)(mp->loopstart + vert_index); + goto finally; + } + /* fall through to regular triangle */ + } + else { + lt = &pMesh->looptri[face_num]; + } +#else + lt = &pMesh->looptri[face_num]; +#endif + loop_index = lt->tri[vert_index]; + +finally: + if (pMesh->precomputedLoopNormals) { + copy_v3_v3(r_no, pMesh->precomputedLoopNormals[loop_index]); + } + else if ((pMesh->mpoly[lt->poly].flag & ME_SMOOTH) == 0) { /* flat */ + if (pMesh->precomputedFaceNormals) { + copy_v3_v3(r_no, pMesh->precomputedFaceNormals[lt->poly]); + } + else { +#ifdef USE_LOOPTRI_DETECT_QUADS + const MPoly *mp = &pMesh->mpoly[lt->poly]; + if (mp->totloop == 4) { + normal_quad_v3(r_no, + pMesh->mvert[pMesh->mloop[mp->loopstart + 0].v].co, + pMesh->mvert[pMesh->mloop[mp->loopstart + 1].v].co, + pMesh->mvert[pMesh->mloop[mp->loopstart + 2].v].co, + pMesh->mvert[pMesh->mloop[mp->loopstart + 3].v].co); + } + else +#endif + { + normal_tri_v3(r_no, + pMesh->mvert[pMesh->mloop[lt->tri[0]].v].co, + pMesh->mvert[pMesh->mloop[lt->tri[1]].v].co, + pMesh->mvert[pMesh->mloop[lt->tri[2]].v].co); + } + } + } + else { + copy_v3_v3(r_no, pMesh->vert_normals[pMesh->mloop[loop_index].v]); + } +} + +static void dm_ts_SetTSpace(const SMikkTSpaceContext *pContext, + const float fvTangent[3], + const float fSign, + const int face_num, + const int vert_index) +{ + // assert(vert_index >= 0 && vert_index < 4); + SGLSLMeshToTangent *pMesh = static_cast<SGLSLMeshToTangent *>(pContext->m_pUserData); + const MLoopTri *lt; + uint loop_index; + +#ifdef USE_LOOPTRI_DETECT_QUADS + if (pMesh->face_as_quad_map) { + lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]]; + const MPoly *mp = &pMesh->mpoly[lt->poly]; + if (mp->totloop == 4) { + loop_index = (uint)(mp->loopstart + vert_index); + goto finally; + } + /* fall through to regular triangle */ + } + else { + lt = &pMesh->looptri[face_num]; + } +#else + lt = &pMesh->looptri[face_num]; +#endif + loop_index = lt->tri[vert_index]; + + float *pRes; + +finally: + pRes = pMesh->tangent[loop_index]; + copy_v3_v3(pRes, fvTangent); + pRes[3] = fSign; +} + +static void DM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata) +{ + SGLSLMeshToTangent *mesh2tangent = static_cast<SGLSLMeshToTangent *>(taskdata); + /* new computation method */ + { + SMikkTSpaceContext sContext{}; + SMikkTSpaceInterface sInterface{}; + + sContext.m_pUserData = mesh2tangent; + sContext.m_pInterface = &sInterface; + sInterface.m_getNumFaces = dm_ts_GetNumFaces; + sInterface.m_getNumVerticesOfFace = dm_ts_GetNumVertsOfFace; + sInterface.m_getPosition = dm_ts_GetPosition; + sInterface.m_getTexCoord = dm_ts_GetTextureCoordinate; + sInterface.m_getNormal = dm_ts_GetNormal; + sInterface.m_setTSpaceBasic = dm_ts_SetTSpace; + sInterface.m_setTSpace = nullptr; + + /* 0 if failed */ + genTangSpaceDefault(&sContext); + } +} + +void BKE_mesh_add_loop_tangent_named_layer_for_uv(CustomData *uv_data, + CustomData *tan_data, + int numLoopData, + const char *layer_name) +{ + if (CustomData_get_named_layer_index(tan_data, CD_TANGENT, layer_name) == -1 && + CustomData_get_named_layer_index(uv_data, CD_MLOOPUV, layer_name) != -1) { + CustomData_add_layer_named( + tan_data, CD_TANGENT, CD_SET_DEFAULT, nullptr, numLoopData, layer_name); + } +} + +void BKE_mesh_calc_loop_tangent_step_0(const CustomData *loopData, + bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], + int tangent_names_count, + bool *rcalc_act, + bool *rcalc_ren, + int *ract_uv_n, + int *rren_uv_n, + char *ract_uv_name, + char *rren_uv_name, + short *rtangent_mask) +{ + /* Active uv in viewport */ + int layer_index = CustomData_get_layer_index(loopData, CD_MLOOPUV); + *ract_uv_n = CustomData_get_active_layer(loopData, CD_MLOOPUV); + ract_uv_name[0] = 0; + if (*ract_uv_n != -1) { + strcpy(ract_uv_name, loopData->layers[*ract_uv_n + layer_index].name); + } + + /* Active tangent in render */ + *rren_uv_n = CustomData_get_render_layer(loopData, CD_MLOOPUV); + rren_uv_name[0] = 0; + if (*rren_uv_n != -1) { + strcpy(rren_uv_name, loopData->layers[*rren_uv_n + layer_index].name); + } + + /* If active tangent not in tangent_names we take it into account */ + *rcalc_act = false; + *rcalc_ren = false; + for (int i = 0; i < tangent_names_count; i++) { + if (tangent_names[i][0] == 0) { + calc_active_tangent = true; + } + } + if (calc_active_tangent) { + *rcalc_act = true; + *rcalc_ren = true; + for (int i = 0; i < tangent_names_count; i++) { + if (STREQ(ract_uv_name, tangent_names[i])) { + *rcalc_act = false; + } + if (STREQ(rren_uv_name, tangent_names[i])) { + *rcalc_ren = false; + } + } + } + *rtangent_mask = 0; + + const int uv_layer_num = CustomData_number_of_layers(loopData, CD_MLOOPUV); + for (int n = 0; n < uv_layer_num; n++) { + const char *name = CustomData_get_layer_name(loopData, CD_MLOOPUV, n); + bool add = false; + for (int i = 0; i < tangent_names_count; i++) { + if (tangent_names[i][0] && STREQ(tangent_names[i], name)) { + add = true; + break; + } + } + if (!add && ((*rcalc_act && ract_uv_name[0] && STREQ(ract_uv_name, name)) || + (*rcalc_ren && rren_uv_name[0] && STREQ(rren_uv_name, name)))) { + add = true; + } + if (add) { + *rtangent_mask |= (short)(1 << n); + } + } + + if (uv_layer_num == 0) { + *rtangent_mask |= DM_TANGENT_MASK_ORCO; + } +} + +void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, + const MPoly *mpoly, + const uint mpoly_len, + const MLoop *mloop, + const MLoopTri *looptri, + const uint looptri_len, + + CustomData *loopdata, + bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], + int tangent_names_len, + const float (*vert_normals)[3], + const float (*poly_normals)[3], + const float (*loop_normals)[3], + const float (*vert_orco)[3], + /* result */ + CustomData *loopdata_out, + const uint loopdata_out_len, + short *tangent_mask_curr_p) +{ + int act_uv_n = -1; + int ren_uv_n = -1; + bool calc_act = false; + bool calc_ren = false; + char act_uv_name[MAX_NAME]; + char ren_uv_name[MAX_NAME]; + short tangent_mask = 0; + short tangent_mask_curr = *tangent_mask_curr_p; + + BKE_mesh_calc_loop_tangent_step_0(loopdata, + calc_active_tangent, + tangent_names, + tangent_names_len, + &calc_act, + &calc_ren, + &act_uv_n, + &ren_uv_n, + act_uv_name, + ren_uv_name, + &tangent_mask); + if ((tangent_mask_curr | tangent_mask) != tangent_mask_curr) { + /* Check we have all the needed layers */ + /* Allocate needed tangent layers */ + for (int i = 0; i < tangent_names_len; i++) { + if (tangent_names[i][0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + loopdata, loopdata_out, (int)loopdata_out_len, tangent_names[i]); + } + } + if ((tangent_mask & DM_TANGENT_MASK_ORCO) && + CustomData_get_named_layer_index(loopdata, CD_TANGENT, "") == -1) { + CustomData_add_layer_named( + loopdata_out, CD_TANGENT, CD_SET_DEFAULT, nullptr, (int)loopdata_out_len, ""); + } + if (calc_act && act_uv_name[0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + loopdata, loopdata_out, (int)loopdata_out_len, act_uv_name); + } + if (calc_ren && ren_uv_name[0]) { + BKE_mesh_add_loop_tangent_named_layer_for_uv( + loopdata, loopdata_out, (int)loopdata_out_len, ren_uv_name); + } + +#ifdef USE_LOOPTRI_DETECT_QUADS + int num_face_as_quad_map; + int *face_as_quad_map = nullptr; + + /* map faces to quads */ + if (looptri_len != mpoly_len) { + /* Over allocate, since we don't know how many ngon or quads we have. */ + + /* map fake face index to looptri */ + face_as_quad_map = static_cast<int *>(MEM_mallocN(sizeof(int) * looptri_len, __func__)); + int k, j; + for (k = 0, j = 0; j < (int)looptri_len; k++, j++) { + face_as_quad_map[k] = j; + /* step over all quads */ + if (mpoly[looptri[j].poly].totloop == 4) { + j++; /* skips the nest looptri */ + } + } + num_face_as_quad_map = k; + } + else { + num_face_as_quad_map = (int)looptri_len; + } +#endif + + /* Calculation */ + if (looptri_len != 0) { + TaskPool *task_pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH); + + tangent_mask_curr = 0; + /* Calculate tangent layers */ + SGLSLMeshToTangent data_array[MAX_MTFACE]; + const int tangent_layer_num = CustomData_number_of_layers(loopdata_out, CD_TANGENT); + for (int n = 0; n < tangent_layer_num; n++) { + int index = CustomData_get_layer_index_n(loopdata_out, CD_TANGENT, n); + BLI_assert(n < MAX_MTFACE); + SGLSLMeshToTangent *mesh2tangent = &data_array[n]; + mesh2tangent->numTessFaces = (int)looptri_len; +#ifdef USE_LOOPTRI_DETECT_QUADS + mesh2tangent->face_as_quad_map = face_as_quad_map; + mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; +#endif + mesh2tangent->mvert = mvert; + mesh2tangent->vert_normals = vert_normals; + mesh2tangent->mpoly = mpoly; + mesh2tangent->mloop = mloop; + mesh2tangent->looptri = looptri; + /* NOTE: we assume we do have tessellated loop normals at this point + * (in case it is object-enabled), have to check this is valid. */ + mesh2tangent->precomputedLoopNormals = loop_normals; + mesh2tangent->precomputedFaceNormals = poly_normals; + + mesh2tangent->orco = nullptr; + mesh2tangent->mloopuv = static_cast<const MLoopUV *>( + CustomData_get_layer_named(loopdata, CD_MLOOPUV, loopdata_out->layers[index].name)); + + /* Fill the resulting tangent_mask */ + if (!mesh2tangent->mloopuv) { + mesh2tangent->orco = vert_orco; + if (!mesh2tangent->orco) { + continue; + } + + tangent_mask_curr |= DM_TANGENT_MASK_ORCO; + } + else { + int uv_ind = CustomData_get_named_layer_index( + loopdata, CD_MLOOPUV, loopdata_out->layers[index].name); + int uv_start = CustomData_get_layer_index(loopdata, CD_MLOOPUV); + BLI_assert(uv_ind != -1 && uv_start != -1); + BLI_assert(uv_ind - uv_start < MAX_MTFACE); + tangent_mask_curr |= (short)(1 << (uv_ind - uv_start)); + } + + mesh2tangent->tangent = static_cast<float(*)[4]>(loopdata_out->layers[index].data); + BLI_task_pool_push(task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, nullptr); + } + + BLI_assert(tangent_mask_curr == tangent_mask); + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + } + else { + tangent_mask_curr = tangent_mask; + } +#ifdef USE_LOOPTRI_DETECT_QUADS + if (face_as_quad_map) { + MEM_freeN(face_as_quad_map); + } +# undef USE_LOOPTRI_DETECT_QUADS + +#endif + + *tangent_mask_curr_p = tangent_mask_curr; + + /* Update active layer index */ + int act_uv_index = (act_uv_n != -1) ? + CustomData_get_layer_index_n(loopdata, CD_MLOOPUV, act_uv_n) : + -1; + if (act_uv_index != -1) { + int tan_index = CustomData_get_named_layer_index( + loopdata, CD_TANGENT, loopdata->layers[act_uv_index].name); + CustomData_set_layer_active_index(loopdata, CD_TANGENT, tan_index); + } /* else tangent has been built from orco */ + + /* Update render layer index */ + int ren_uv_index = (ren_uv_n != -1) ? + CustomData_get_layer_index_n(loopdata, CD_MLOOPUV, ren_uv_n) : + -1; + if (ren_uv_index != -1) { + int tan_index = CustomData_get_named_layer_index( + loopdata, CD_TANGENT, loopdata->layers[ren_uv_index].name); + CustomData_set_layer_render_index(loopdata, CD_TANGENT, tan_index); + } /* else tangent has been built from orco */ + } +} + +void BKE_mesh_calc_loop_tangents(Mesh *me_eval, + bool calc_active_tangent, + const char (*tangent_names)[MAX_NAME], + int tangent_names_len) +{ + BKE_mesh_runtime_looptri_ensure(me_eval); + + /* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */ + short tangent_mask = 0; + BKE_mesh_calc_loop_tangent_ex( + BKE_mesh_vertices(me_eval), + BKE_mesh_polygons(me_eval), + (uint)me_eval->totpoly, + BKE_mesh_loops(me_eval), + me_eval->runtime.looptris.array, + (uint)me_eval->runtime.looptris.len, + &me_eval->ldata, + calc_active_tangent, + tangent_names, + tangent_names_len, + BKE_mesh_vertex_normals_ensure(me_eval), + BKE_mesh_poly_normals_ensure(me_eval), + static_cast<const float(*)[3]>(CustomData_get_layer(&me_eval->ldata, CD_NORMAL)), + /* may be nullptr */ + static_cast<const float(*)[3]>(CustomData_get_layer(&me_eval->vdata, CD_ORCO)), + /* result */ + &me_eval->ldata, + (uint)me_eval->totloop, + &tangent_mask); +} + +/** \} */ |