diff --git a/source/blender/blenkernel/BKE_editmesh_tangent.hh b/source/blender/blenkernel/BKE_editmesh_tangent.hh index 0281fcf8fd4..ed95d64c2e6 100644 --- a/source/blender/blenkernel/BKE_editmesh_tangent.hh +++ b/source/blender/blenkernel/BKE_editmesh_tangent.hh @@ -8,27 +8,24 @@ * \ingroup bke */ +#include "BLI_array.hh" #include "BLI_math_vector_types.hh" #include "BLI_span.hh" - -#include "DNA_customdata_types.h" +#include "BLI_string_ref.hh" struct BMEditMesh; /** * \see #BKE_mesh_calc_loop_tangent, same logic but used arrays instead of #BMesh data. - * - * \note This function is not so normal, its using #BMesh.ldata as input, - * but output's to #Mesh.corner_data. - * This is done because #CD_TANGENT is cache data used only for drawing. */ -void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME], - int tangent_names_len, - blender::Span face_normals, - blender::Span corner_normals, - blender::Span vert_orco, - CustomData *dm_loopdata_out, - uint dm_loopdata_out_len, - short *tangent_mask_curr_p); +blender::Array> BKE_editmesh_uv_tangents_calc( + BMEditMesh *em, + blender::Span face_normals, + blender::Span corner_normals, + blender::Span uv_names); + +blender::Array BKE_editmesh_orco_tangents_calc( + BMEditMesh *em, + blender::Span face_normals, + blender::Span corner_normals, + blender::Span vert_orco); diff --git a/source/blender/blenkernel/BKE_mesh_legacy_derived_mesh.hh b/source/blender/blenkernel/BKE_mesh_legacy_derived_mesh.hh index e296e21af05..769edc4d58e 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_derived_mesh.hh +++ b/source/blender/blenkernel/BKE_mesh_legacy_derived_mesh.hh @@ -69,8 +69,6 @@ struct DerivedMesh { /* Always owned by this object. */ int *face_offsets; - short tangent_mask; /* which tangent layers are calculated */ - /* Misc. Queries */ /* Also called in Editmode */ diff --git a/source/blender/blenkernel/BKE_mesh_runtime.hh b/source/blender/blenkernel/BKE_mesh_runtime.hh index 03ab1da83a4..58996f736b9 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.hh +++ b/source/blender/blenkernel/BKE_mesh_runtime.hh @@ -33,8 +33,6 @@ void BKE_mesh_runtime_ensure_edit_data(Mesh *mesh); * * For "smaller" changes to meshes like updating positions, consider calling a more specific update * function like #Mesh::tag_positions_changed(). - * - * Also note that some derived caches like #CD_TANGENT are stored directly in #CustomData. */ void BKE_mesh_runtime_clear_geometry(Mesh *mesh); diff --git a/source/blender/blenkernel/BKE_mesh_tangent.hh b/source/blender/blenkernel/BKE_mesh_tangent.hh index 8182612fab7..a13c627f42c 100644 --- a/source/blender/blenkernel/BKE_mesh_tangent.hh +++ b/source/blender/blenkernel/BKE_mesh_tangent.hh @@ -7,11 +7,9 @@ * \ingroup bke */ -#include "DNA_customdata_types.h" - +#include "BLI_array.hh" #include "BLI_math_vector_types.hh" #include "BLI_offset_indices.hh" -#include "BLI_sys_types.h" struct ReportList; struct Mesh; @@ -44,54 +42,31 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, float (*r_looptangents)[4], ReportList *reports); +namespace blender::bke::mesh { + /** - * See: #BKE_editmesh_loop_tangent_calc (matching logic). + * See: #BKE_editmesh_uv_tangents_calc (matching logic). */ -void BKE_mesh_calc_loop_tangent_ex(blender::Span vert_positions, - blender::OffsetIndices faces, - blender::Span corner_verts, - blender::Span corner_tris, - blender::Span corner_tri_faces, - blender::Span sharp_faces, - const CustomData *loopdata, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME], - int tangent_names_len, - blender::Span vert_normals, - blender::Span face_normals, - blender::Span corner_normals, - blender::Span vert_orco, - /* result */ - CustomData *loopdata_out, - uint loopdata_out_len, - short *tangent_mask_curr_p); +Array> calc_uv_tangents(Span vert_positions, + OffsetIndices faces, + Span corner_verts, + Span corner_tris, + Span corner_tri_faces, + Span sharp_faces, + Span vert_normals, + Span face_normals, + Span corner_normals, + Span> uv_maps); -void BKE_mesh_calc_loop_tangents(Mesh *mesh_eval, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME], - int tangent_names_len); +Array calc_orco_tangents(Span vert_positions, + OffsetIndices faces, + Span corner_verts, + Span corner_tris, + Span corner_tri_faces, + Span sharp_faces, + Span vert_normals, + Span face_normals, + Span corner_normals, + Span vert_orco); -/* Helpers */ -void BKE_mesh_add_loop_tangent_named_layer_for_uv(const CustomData *uv_data, - CustomData *tan_data, - int numLoopData, - const char *layer_name); - -#define DM_TANGENT_MASK_ORCO (1 << 9) -/** - * Here we get some useful information such as active uv layer name and - * search if it is already in tangent_names. - * Also, we calculate tangent_mask that works as a descriptor of tangents state. - * If tangent_mask has changed, then recalculate tangents. - */ -void BKE_mesh_calc_loop_tangent_step_0(const CustomData *loopData, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_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); +} // namespace blender::bke::mesh diff --git a/source/blender/blenkernel/intern/attribute_legacy_convert.cc b/source/blender/blenkernel/intern/attribute_legacy_convert.cc index 84af41053b4..34b568bf96a 100644 --- a/source/blender/blenkernel/intern/attribute_legacy_convert.cc +++ b/source/blender/blenkernel/intern/attribute_legacy_convert.cc @@ -23,6 +23,7 @@ std::optional custom_data_type_to_attr_type(const eCustomDataType data switch (data_type) { case CD_NUMTYPES: case CD_AUTO_FROM_NAME: + case CD_TANGENT: /* These type is not used for actual #CustomData layers. */ BLI_assert_unreachable(); return std::nullopt; @@ -57,7 +58,6 @@ std::optional custom_data_type_to_attr_type(const eCustomDataType data case CD_NORMAL: case CD_ORIGSPACE: case CD_ORCO: - case CD_TANGENT: case CD_MDISPS: case CD_CLOTH_ORCO: case CD_ORIGSPACE_MLOOP: diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 318e4ee2036..73425430dc8 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1759,17 +1759,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr, nullptr, nullptr}, - /* 18: CD_TANGENT */ - {sizeof(float[4]), - alignof(float[4]), - "", - 0, - N_("Tangent"), - nullptr, - nullptr, - nullptr, - nullptr, - nullptr}, + /* 18: CD_TANGENT */ /* DEPRECATED */ + {sizeof(float[4]), alignof(float[4]), "", 0, N_("Tangent")}, /* 19: CD_MDISPS */ {sizeof(MDisps), alignof(MDisps), @@ -2202,7 +2193,7 @@ const CustomData_MeshMasks CD_MASK_DERIVEDMESH = { CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL), /*emask*/ (CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL), - /*fmask*/ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_TANGENT), + /*fmask*/ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE), /*pmask*/ (CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL), /*lmask*/ @@ -2226,7 +2217,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = { (CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL), /*fmask*/ (CD_MASK_MFACE | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_MTFACE | CD_MASK_MCOL | - CD_MASK_ORIGSPACE | CD_MASK_TANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_PROP_ALL), + CD_MASK_ORIGSPACE | CD_MASK_TESSLOOPNORMAL | CD_MASK_PROP_ALL), /*pmask*/ (CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_PROP_ALL), /*lmask*/ diff --git a/source/blender/blenkernel/intern/editmesh_tangent.cc b/source/blender/blenkernel/intern/editmesh_tangent.cc index d725a2dbb73..a9bd7f889f5 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.cc +++ b/source/blender/blenkernel/intern/editmesh_tangent.cc @@ -8,24 +8,25 @@ #include "BLI_math_geom.h" #include "BLI_math_vector.h" -#include "BLI_task.h" +#include "BLI_task.hh" #include "DNA_customdata_types.h" -#include "DNA_defs.h" #include "BKE_customdata.hh" #include "BKE_editmesh.hh" #include "BKE_editmesh_tangent.hh" #include "BKE_mesh.hh" -#include "BKE_mesh_tangent.hh" /* for utility functions */ #include "MEM_guardedalloc.h" /* interface */ #include "mikktspace.hh" +using blender::Array; using blender::float3; +using blender::float4; using blender::Span; +using blender::StringRef; /* -------------------------------------------------------------------- */ /** \name Tangent Space Calculation @@ -151,186 +152,136 @@ struct SGLSLEditMeshToTangent { #endif }; -static void emDM_calc_loop_tangents_thread(TaskPool *__restrict /*pool*/, void *taskdata) +static void calc_face_as_quad_map( + BMEditMesh *&em, BMesh *&bm, int &totface, int &num_face_as_quad_map, int *&face_as_quad_map) { - SGLSLEditMeshToTangent *mesh_data = static_cast(taskdata); +#ifdef USE_LOOPTRI_DETECT_QUADS + if (em->looptris.size() != bm->totface) { + /* Over allocate, since we don't know how many ngon or quads we have. */ - mikk::Mikktspace mikk(*mesh_data); - mikk.genTangSpace(); + /* map fake face index to looptri */ + face_as_quad_map = MEM_malloc_arrayN(size_t(totface), __func__); + int i, j; + for (i = 0, j = 0; j < totface; i++, j++) { + face_as_quad_map[i] = j; + /* step over all quads */ + if (em->looptris[j][0]->f->len == 4) { + j++; /* Skips the next looptri. */ + } + } + num_face_as_quad_map = i; + } + else { + num_face_as_quad_map = totface; + } +#else + num_face_as_quad_map = totface; + face_as_quad_map = nullptr; +#endif } -void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME], - int tangent_names_len, - const Span face_normals, - const Span corner_normals, - const Span vert_orco, - /* result */ - CustomData *loopdata_out, - const uint loopdata_out_len, - short *tangent_mask_curr_p) +Array> BKE_editmesh_uv_tangents_calc(BMEditMesh *em, + const Span face_normals, + const Span corner_normals, + const Span uv_names) { - BMesh *bm = em->bm; - - 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(&bm->ldata, - 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) { - for (int i = 0; i < tangent_names_len; i++) { - if (tangent_names[i][0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - &bm->ldata, loopdata_out, int(loopdata_out_len), tangent_names[i]); - } - } - if ((tangent_mask & DM_TANGENT_MASK_ORCO) && - CustomData_get_named_layer_index(loopdata_out, CD_TANGENT, "") == -1) - { - CustomData_add_layer_named( - loopdata_out, CD_TANGENT, CD_SET_DEFAULT, int(loopdata_out_len), ""); - } - if (calc_act && act_uv_name[0]) { - BKE_mesh_add_loop_tangent_named_layer_for_uv( - &bm->ldata, 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( - &bm->ldata, loopdata_out, int(loopdata_out_len), ren_uv_name); - } - int totface = em->looptris.size(); -#ifdef USE_LOOPTRI_DETECT_QUADS - int num_face_as_quad_map; - int *face_as_quad_map = nullptr; - - /* map faces to quads */ - if (em->looptris.size() != bm->totface) { - /* Over allocate, since we don't know how many ngon or quads we have. */ - - /* map fake face index to looptri */ - face_as_quad_map = MEM_malloc_arrayN(size_t(totface), __func__); - int i, j; - for (i = 0, j = 0; j < totface; i++, j++) { - face_as_quad_map[i] = j; - /* step over all quads */ - if (em->looptris[j][0]->f->len == 4) { - j++; /* Skips the next looptri. */ - } - } - num_face_as_quad_map = i; - } - else { - num_face_as_quad_map = totface; - } -#endif - /* Calculation */ - if (em->looptris.size() != 0) { - TaskPool *task_pool; - task_pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH); - - tangent_mask_curr = 0; - /* Calculate tangent layers */ - SGLSLEditMeshToTangent data_array[MAX_MTFACE]; - int index = 0; - int n = 0; - CustomData_update_typemap(loopdata_out); - const int tangent_layer_num = CustomData_number_of_layers(loopdata_out, CD_TANGENT); - for (n = 0; n < tangent_layer_num; n++) { - index = CustomData_get_layer_index_n(loopdata_out, CD_TANGENT, n); - BLI_assert(n < MAX_MTFACE); - SGLSLEditMeshToTangent *mesh2tangent = &data_array[n]; - mesh2tangent->numTessFaces = em->looptris.size(); -#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->face_normals = face_normals; - /* 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->corner_normals = corner_normals; - mesh2tangent->cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_PROP_FLOAT2, n); - - /* needed for indexing loop-tangents */ - int htype_index = BM_LOOP; - if (mesh2tangent->cd_loop_uv_offset == -1) { - mesh2tangent->orco = vert_orco; - if (mesh2tangent->orco.is_empty()) { - continue; - } - /* needed for orco lookups */ - htype_index |= BM_VERT; - tangent_mask_curr |= DM_TANGENT_MASK_ORCO; - } - else { - /* Fill the resulting tangent_mask */ - int uv_ind = CustomData_get_named_layer_index( - &bm->ldata, CD_PROP_FLOAT2, loopdata_out->layers[index].name); - int uv_start = CustomData_get_layer_index(&bm->ldata, CD_PROP_FLOAT2); - BLI_assert(uv_ind != -1 && uv_start != -1); - BLI_assert(uv_ind - uv_start < MAX_MTFACE); - tangent_mask_curr |= 1 << (uv_ind - uv_start); - } - if (!mesh2tangent->face_normals.is_empty()) { - /* needed for face normal lookups */ - htype_index |= BM_FACE; - } - BM_mesh_elem_index_ensure(bm, htype_index); - - mesh2tangent->looptris = em->looptris; - mesh2tangent->tangent = static_cast(loopdata_out->layers[index].data); - - BLI_task_pool_push( - task_pool, emDM_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 + using namespace blender; + if (em->looptris.is_empty()) { + return {}; } - *tangent_mask_curr_p = tangent_mask_curr; + BMesh *bm = em->bm; - int act_uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_PROP_FLOAT2, act_uv_n); - if (act_uv_index >= 0) { - int tan_index = CustomData_get_named_layer_index( - loopdata_out, CD_TANGENT, bm->ldata.layers[act_uv_index].name); - CustomData_set_layer_active_index(loopdata_out, CD_TANGENT, tan_index); - } /* else tangent has been built from orco */ + int totface = em->looptris.size(); + int num_face_as_quad_map; + int *face_as_quad_map = nullptr; + calc_face_as_quad_map(em, bm, totface, num_face_as_quad_map, face_as_quad_map); - /* Update render layer index */ - int ren_uv_index = CustomData_get_layer_index_n(&bm->ldata, CD_PROP_FLOAT2, ren_uv_n); - if (ren_uv_index >= 0) { - int tan_index = CustomData_get_named_layer_index( - loopdata_out, CD_TANGENT, bm->ldata.layers[ren_uv_index].name); - CustomData_set_layer_render_index(loopdata_out, CD_TANGENT, tan_index); - } /* else tangent has been built from orco */ + Array> result(uv_names.size()); + + /* needed for indexing loop-tangents */ + int htype_index = BM_LOOP; + if (!face_normals.is_empty()) { + /* needed for face normal lookups */ + htype_index |= BM_FACE; + } + BM_mesh_elem_index_ensure(bm, htype_index); + + threading::parallel_for(uv_names.index_range(), 1, [&](const IndexRange range) { + for (const int n : range) { + SGLSLEditMeshToTangent mesh2tangent{}; + mesh2tangent.numTessFaces = em->looptris.size(); + mesh2tangent.face_as_quad_map = face_as_quad_map; + mesh2tangent.num_face_as_quad_map = num_face_as_quad_map; + mesh2tangent.face_normals = face_normals; + /* 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.corner_normals = corner_normals; + mesh2tangent.cd_loop_uv_offset = CustomData_get_offset_named( + &bm->ldata, CD_PROP_FLOAT2, uv_names[n]); + BLI_assert(mesh2tangent.cd_loop_uv_offset != -1); + + mesh2tangent.looptris = em->looptris; + result[n].reinitialize(bm->totloop); + mesh2tangent.tangent = reinterpret_cast(result[n].data()); + + mikk::Mikktspace mikk(mesh2tangent); + mikk.genTangSpace(); + } + }); + + MEM_SAFE_FREE(face_as_quad_map); + + return result; +} + +Array BKE_editmesh_orco_tangents_calc(BMEditMesh *em, + const Span face_normals, + const Span corner_normals, + const Span vert_orco) +{ + if (em->looptris.is_empty()) { + return {}; + } + + BMesh *bm = em->bm; + + int totface = em->looptris.size(); + int num_face_as_quad_map; + int *face_as_quad_map = nullptr; + calc_face_as_quad_map(em, bm, totface, num_face_as_quad_map, face_as_quad_map); + + Array result(bm->totloop); + + /* needed for indexing loop-tangents */ + int htype_index = BM_LOOP; + /* needed for orco lookups */ + htype_index |= BM_VERT; + if (!face_normals.is_empty()) { + /* needed for face normal lookups */ + htype_index |= BM_FACE; + } + BM_mesh_elem_index_ensure(bm, htype_index); + + SGLSLEditMeshToTangent mesh2tangent{}; + mesh2tangent.numTessFaces = em->looptris.size(); + mesh2tangent.face_as_quad_map = face_as_quad_map; + mesh2tangent.num_face_as_quad_map = num_face_as_quad_map; + mesh2tangent.face_normals = face_normals; + /* 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.corner_normals = corner_normals; + mesh2tangent.cd_loop_uv_offset = -1; + mesh2tangent.orco = vert_orco; + + mesh2tangent.looptris = em->looptris; + mesh2tangent.tangent = reinterpret_cast(result.data()); + mikk::Mikktspace mikk(mesh2tangent); + mikk.genTangSpace(); + + MEM_SAFE_FREE(face_as_quad_map); + + return result; } /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_flip_faces.cc b/source/blender/blenkernel/intern/mesh_flip_faces.cc index 2bff9ccf896..739ac99149f 100644 --- a/source/blender/blenkernel/intern/mesh_flip_faces.cc +++ b/source/blender/blenkernel/intern/mesh_flip_faces.cc @@ -57,7 +57,6 @@ void mesh_flip_faces(Mesh &mesh, const IndexMask &selection) } }); - flip_custom_data_type(faces, mesh.corner_data, selection, CD_TANGENT); flip_custom_data_type(faces, mesh.corner_data, selection, CD_MLOOPTANGENT); flip_custom_data_type(faces, mesh.corner_data, selection, CD_GRID_PAINT_MASK); flip_custom_data_type(faces, mesh.corner_data, selection, CD_ORIGSPACE_MLOOP); diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 563a2e938bd..a3412f790c2 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -650,9 +650,6 @@ static bool check_matching_legacy_layer_counts(CustomData *fdata_legacy, if (!LAYER_CMP(ldata, CD_NORMAL, fdata_legacy, CD_TESSLOOPNORMAL)) { return false; } - if (!LAYER_CMP(ldata, CD_TANGENT, fdata_legacy, CD_TANGENT)) { - return false; - } # undef LAYER_CMP @@ -684,10 +681,6 @@ static void add_mface_layers(Mesh &mesh, CustomData *fdata_legacy, CustomData *l CustomData_add_layer_named( fdata_legacy, CD_TESSLOOPNORMAL, CD_SET_DEFAULT, total, ldata->layers[i].name); } - else if (ldata->layers[i].type == CD_TANGENT) { - CustomData_add_layer_named( - fdata_legacy, CD_TANGENT, CD_SET_DEFAULT, total, ldata->layers[i].name); - } } update_active_fdata_layers(mesh, fdata_legacy, ldata); @@ -860,7 +853,6 @@ static void mesh_loops_to_tessdata(CustomData *fdata_legacy, const int numCol = CustomData_number_of_layers(corner_data, CD_PROP_BYTE_COLOR); const bool hasOrigSpace = CustomData_has_layer(corner_data, CD_ORIGSPACE_MLOOP); const bool hasLoopNormal = CustomData_has_layer(corner_data, CD_NORMAL); - const bool hasLoopTangent = CustomData_has_layer(corner_data, CD_TANGENT); int findex, i, j; const int *pidx; uint(*lidx)[4]; @@ -917,21 +909,6 @@ static void mesh_loops_to_tessdata(CustomData *fdata_legacy, } } } - - if (hasLoopTangent) { - /* Need to do for all UV maps at some point. */ - float(*ftangents)[4] = (float(*)[4])CustomData_get_layer(fdata_legacy, CD_TANGENT); - const float(*ltangents)[4] = (const float(*)[4])CustomData_get_layer(corner_data, 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]]); - } - } - } } int BKE_mesh_mface_index_validate(MFace *mface, CustomData *fdata_legacy, int mfindex, int nr) diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index 90bc9b5e356..822dd88f657 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -12,9 +12,6 @@ #include "BLI_math_geom.h" #include "BLI_math_vector.h" -#include "BLI_string.h" -#include "BLI_task.h" -#include "BLI_utildefines.h" #include "BKE_attribute.hh" #include "BKE_customdata.hh" @@ -26,11 +23,14 @@ #include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */ +using blender::Array; using blender::float2; using blender::float3; +using blender::float4; using blender::int3; using blender::OffsetIndices; using blender::Span; +using blender::StringRef; /* -------------------------------------------------------------------- */ /** \name Mesh Tangent Calculations (Single Layer) @@ -160,6 +160,8 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh, /* Necessary complexity to handle corner_tris as quads for correct tangents. */ #define USE_TRI_DETECT_QUADS +namespace blender::bke::mesh { + struct SGLSLMeshToTangent { uint GetNumFaces() { @@ -288,14 +290,14 @@ struct SGLSLMeshToTangent { bool has_uv() const { - return mloopuv != nullptr; + return !mloopuv.is_empty(); } Span face_normals; Span corner_normals; const int3 *corner_tris; const int *tri_faces; - const float2 *mloopuv; /* texture coordinates */ + Span mloopuv; /* texture coordinates */ OffsetIndices faces; const int *corner_verts; /* indices */ Span positions; /* vertex coordinates */ @@ -313,313 +315,138 @@ struct SGLSLMeshToTangent { #endif }; -static void DM_calc_loop_tangents_thread(TaskPool *__restrict /*pool*/, void *taskdata) +static void calc_face_as_quad_map(const OffsetIndices &faces, + const Span &corner_tris, + const Span &corner_tri_faces, + int &num_face_as_quad_map, + int *&face_as_quad_map) { - SGLSLMeshToTangent *mesh_data = static_cast(taskdata); +#ifdef USE_TRI_DETECT_QUADS + if (corner_tris.size() != faces.size()) { + /* Over allocate, since we don't know how many ngon or quads we have. */ - mikk::Mikktspace mikk(*mesh_data); + /* Map fake face index to corner_tris. */ + face_as_quad_map = MEM_malloc_arrayN(size_t(corner_tris.size()), __func__); + int k, j; + for (k = 0, j = 0; j < int(corner_tris.size()); k++, j++) { + face_as_quad_map[k] = j; + /* step over all quads */ + if (faces[corner_tri_faces[j]].size() == 4) { + j++; /* Skips the next corner_tri. */ + } + } + num_face_as_quad_map = k; + } + else { + num_face_as_quad_map = int(corner_tris.size()); + } +#else + num_face_as_quad_map = 0; + face_as_quad_map = nullptr; +#endif +} + +Array> calc_uv_tangents(const Span vert_positions, + const OffsetIndices faces, + const Span corner_verts, + const Span corner_tris, + const Span corner_tri_faces, + const Span sharp_faces, + const Span vert_normals, + const Span face_normals, + const Span corner_normals, + const Span> uv_maps) +{ + if (corner_tris.is_empty()) { + return {}; + } + + int num_face_as_quad_map; + int *face_as_quad_map = nullptr; + calc_face_as_quad_map( + faces, corner_tris, corner_tri_faces, num_face_as_quad_map, face_as_quad_map); + + Array> results(uv_maps.size()); + threading::parallel_for(uv_maps.index_range(), 1, [&](const IndexRange range) { + for (const int64_t i : range) { + SGLSLMeshToTangent mesh2tangent{}; + mesh2tangent.numTessFaces = int(corner_tris.size()); + mesh2tangent.face_as_quad_map = face_as_quad_map; + mesh2tangent.num_face_as_quad_map = num_face_as_quad_map; + mesh2tangent.positions = vert_positions; + mesh2tangent.vert_normals = vert_normals; + mesh2tangent.faces = faces; + mesh2tangent.corner_verts = corner_verts.data(); + mesh2tangent.corner_tris = corner_tris.data(); + mesh2tangent.tri_faces = corner_tri_faces.data(); + mesh2tangent.sharp_faces = sharp_faces; + /* 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.corner_normals = corner_normals; + mesh2tangent.face_normals = face_normals; + mesh2tangent.mloopuv = uv_maps[i]; + + results[i].reinitialize(corner_verts.size()); + mesh2tangent.tangent = reinterpret_cast(results[i].data()); + + mikk::Mikktspace mikk(mesh2tangent); + mikk.genTangSpace(); + } + }); + + MEM_SAFE_FREE(face_as_quad_map); + + return results; +} + +Array calc_orco_tangents(const Span vert_positions, + const OffsetIndices faces, + const Span corner_verts, + const Span corner_tris, + const Span corner_tri_faces, + const Span sharp_faces, + const Span vert_normals, + const Span face_normals, + const Span corner_normals, + const Span vert_orco) +{ + if (corner_tris.is_empty()) { + return {}; + } + + int num_face_as_quad_map; + int *face_as_quad_map = nullptr; + calc_face_as_quad_map( + faces, corner_tris, corner_tri_faces, num_face_as_quad_map, face_as_quad_map); + + Array results(corner_verts.size()); + SGLSLMeshToTangent mesh2tangent{}; + mesh2tangent.numTessFaces = int(corner_tris.size()); + mesh2tangent.face_as_quad_map = face_as_quad_map; + mesh2tangent.num_face_as_quad_map = num_face_as_quad_map; + mesh2tangent.positions = vert_positions; + mesh2tangent.vert_normals = vert_normals; + mesh2tangent.faces = faces; + mesh2tangent.corner_verts = corner_verts.data(); + mesh2tangent.corner_tris = corner_tris.data(); + mesh2tangent.tri_faces = corner_tri_faces.data(); + mesh2tangent.sharp_faces = sharp_faces; + /* 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.corner_normals = corner_normals; + mesh2tangent.face_normals = face_normals; + mesh2tangent.orco = vert_orco; + + mesh2tangent.tangent = reinterpret_cast(results.data()); + + mikk::Mikktspace mikk(mesh2tangent); mikk.genTangSpace(); + + MEM_SAFE_FREE(face_as_quad_map); + + return results; } -void BKE_mesh_add_loop_tangent_named_layer_for_uv(const 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_PROP_FLOAT2, layer_name) != -1) - { - CustomData_add_layer_named(tan_data, CD_TANGENT, CD_SET_DEFAULT, numLoopData, layer_name); - } -} - -void BKE_mesh_calc_loop_tangent_step_0(const CustomData *loopData, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_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_PROP_FLOAT2); - *ract_uv_n = CustomData_get_active_layer(loopData, CD_PROP_FLOAT2); - ract_uv_name[0] = 0; - if (*ract_uv_n != -1) { - BLI_strncpy( - ract_uv_name, loopData->layers[*ract_uv_n + layer_index].name, MAX_CUSTOMDATA_LAYER_NAME); - } - - /* Active tangent in render */ - *rren_uv_n = CustomData_get_render_layer(loopData, CD_PROP_FLOAT2); - rren_uv_name[0] = 0; - if (*rren_uv_n != -1) { - BLI_strncpy( - rren_uv_name, loopData->layers[*rren_uv_n + layer_index].name, MAX_CUSTOMDATA_LAYER_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_PROP_FLOAT2); - for (int n = 0; n < uv_layer_num; n++) { - const char *name = CustomData_get_layer_name(loopData, CD_PROP_FLOAT2, 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 Span vert_positions, - const OffsetIndices faces, - const Span corner_verts, - const Span corner_tris, - const Span corner_tri_faces, - const Span sharp_faces, - const CustomData *loopdata, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME], - int tangent_names_len, - const Span vert_normals, - const Span face_normals, - const Span corner_normals, - const Span vert_orco, - /* 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_CUSTOMDATA_LAYER_NAME]; - char ren_uv_name[MAX_CUSTOMDATA_LAYER_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, 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_TRI_DETECT_QUADS - int num_face_as_quad_map; - int *face_as_quad_map = nullptr; - - /* map faces to quads */ - if (corner_tris.size() != faces.size()) { - /* Over allocate, since we don't know how many ngon or quads we have. */ - - /* Map fake face index to corner_tris. */ - face_as_quad_map = MEM_malloc_arrayN(size_t(corner_tris.size()), __func__); - int k, j; - for (k = 0, j = 0; j < int(corner_tris.size()); k++, j++) { - face_as_quad_map[k] = j; - /* step over all quads */ - if (faces[corner_tri_faces[j]].size() == 4) { - j++; /* Skips the next corner_tri. */ - } - } - num_face_as_quad_map = k; - } - else { - num_face_as_quad_map = int(corner_tris.size()); - } -#endif - - /* Calculation */ - if (corner_tris.size() != 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(corner_tris.size()); -#ifdef USE_TRI_DETECT_QUADS - mesh2tangent->face_as_quad_map = face_as_quad_map; - mesh2tangent->num_face_as_quad_map = num_face_as_quad_map; -#endif - mesh2tangent->positions = vert_positions; - mesh2tangent->vert_normals = vert_normals; - mesh2tangent->faces = faces; - mesh2tangent->corner_verts = corner_verts.data(); - mesh2tangent->corner_tris = corner_tris.data(); - mesh2tangent->tri_faces = corner_tri_faces.data(); - mesh2tangent->sharp_faces = sharp_faces; - /* 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->corner_normals = corner_normals; - mesh2tangent->face_normals = face_normals; - - mesh2tangent->orco = {}; - mesh2tangent->mloopuv = static_cast(CustomData_get_layer_named( - loopdata, CD_PROP_FLOAT2, loopdata_out->layers[index].name)); - - /* Fill the resulting tangent_mask */ - if (!mesh2tangent->mloopuv) { - mesh2tangent->orco = vert_orco; - if (mesh2tangent->orco.is_empty()) { - continue; - } - - tangent_mask_curr |= DM_TANGENT_MASK_ORCO; - } - else { - int uv_ind = CustomData_get_named_layer_index( - loopdata, CD_PROP_FLOAT2, loopdata_out->layers[index].name); - int uv_start = CustomData_get_layer_index(loopdata, CD_PROP_FLOAT2); - 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(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_TRI_DETECT_QUADS - if (face_as_quad_map) { - MEM_freeN(face_as_quad_map); - } -# undef USE_TRI_DETECT_QUADS - -#endif - - *tangent_mask_curr_p = tangent_mask_curr; - - /* Update active layer index */ - if (const char *active_uv_name = CustomData_get_active_layer_name(loopdata, CD_PROP_FLOAT2)) { - int tan_index = CustomData_get_named_layer_index(loopdata_out, CD_TANGENT, active_uv_name); - if (tan_index != -1) { - CustomData_set_layer_active_index(loopdata_out, CD_TANGENT, tan_index); - } - } /* else tangent has been built from orco */ - - /* Update render layer index */ - if (const char *render_uv_name = CustomData_get_render_layer_name(loopdata, CD_PROP_FLOAT2)) { - int tan_index = CustomData_get_named_layer_index(loopdata_out, CD_TANGENT, render_uv_name); - if (tan_index != -1) { - CustomData_set_layer_render_index(loopdata_out, CD_TANGENT, tan_index); - } - } /* else tangent has been built from orco */ - } -} - -void BKE_mesh_calc_loop_tangents(Mesh *mesh_eval, - bool calc_active_tangent, - const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME], - int tangent_names_len) -{ - /* TODO(@ideasman42): store in Mesh.runtime to avoid recalculation. */ - using namespace blender; - using namespace blender::bke; - const Span corner_tris = mesh_eval->corner_tris(); - const bke::AttributeAccessor attributes = mesh_eval->attributes(); - const VArraySpan sharp_face = *attributes.lookup("sharp_face", AttrDomain::Face); - const float3 *orco = static_cast( - CustomData_get_layer(&mesh_eval->vert_data, CD_ORCO)); - short tangent_mask = 0; - BKE_mesh_calc_loop_tangent_ex(mesh_eval->vert_positions(), - mesh_eval->faces(), - mesh_eval->corner_verts(), - corner_tris, - mesh_eval->corner_tri_faces(), - sharp_face, - &mesh_eval->corner_data, - calc_active_tangent, - tangent_names, - tangent_names_len, - mesh_eval->vert_normals(), - mesh_eval->face_normals(), - mesh_eval->corner_normals(), - /* may be nullptr */ - orco ? Span(orco, mesh_eval->verts_num) : Span(), - /* result */ - &mesh_eval->corner_data, - uint(mesh_eval->corners_num), - &tangent_mask); -} +} // namespace blender::bke::mesh /** \} */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index f2af345940d..d00fe40c0e8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -12,6 +12,7 @@ #include "GPU_attribute_convert.hh" +#include "BKE_attribute.hh" #include "BKE_editmesh_tangent.hh" #include "BKE_mesh.hh" #include "BKE_mesh_tangent.hh" @@ -22,30 +23,18 @@ namespace blender::draw { -static void extract_tan_init_common(const MeshRenderData &mr, - const MeshBatchCache &cache, - GPUVertFormat *format, - gpu::VertAttrType gpu_attr_type, - CustomData *r_loop_data, - int *r_v_len, - int *r_tan_len, - char r_tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME], - bool *r_use_orco_tan) +static Array> extract_tan_init_common(const MeshRenderData &mr, + const MeshBatchCache &cache, + GPUVertFormat *format, + gpu::VertAttrType gpu_attr_type) { GPU_vertformat_deinterleave(format); const CustomData *cd_ldata = (mr.extract_type == MeshExtractType::BMesh) ? &mr.bm->ldata : &mr.mesh->corner_data; - const CustomData *cd_vdata = (mr.extract_type == MeshExtractType::BMesh) ? &mr.bm->vdata : - &mr.mesh->vert_data; uint32_t tan_layers = cache.cd_used.tan; - const float3 *orco_ptr = static_cast(CustomData_get_layer(cd_vdata, CD_ORCO)); - Span orco = orco_ptr ? Span(orco_ptr, mr.verts_num) : Span(); - Array orco_allocated; bool use_orco_tan = cache.cd_used.tan_orco != 0; - int tan_len = 0; - /* FIXME(#91838): This is to avoid a crash when orco tangent was requested but there are valid * uv layers. It would be better to fix the root cause. */ if (tan_layers == 0 && use_orco_tan && @@ -55,6 +44,50 @@ static void extract_tan_init_common(const MeshRenderData &mr, use_orco_tan = false; } + if (use_orco_tan) { + Array tangents; + if (mr.extract_type == MeshExtractType::BMesh) { + Array positions = BM_mesh_vert_coords_alloc(mr.bm); + tangents = BKE_editmesh_orco_tangents_calc( + mr.edit_bmesh, mr.bm_face_normals, mr.bm_loop_normals, positions); + } + else { + Span orco; + Array orco_allocated; + if (const float3 *orco_ptr = static_cast( + CustomData_get_layer(&mr.mesh->vert_data, CD_ORCO))) + { + orco = Span(orco_ptr, mr.verts_num); + } + else { + orco_allocated = mr.vert_positions; + /* TODO: This is not thread-safe. Draw extraction should not modify the mesh. */ + BKE_mesh_orco_verts_transform(const_cast(mr.mesh), orco_allocated, false); + orco = orco_allocated; + } + tangents = bke::mesh::calc_orco_tangents(mr.vert_positions, + mr.faces, + mr.corner_verts, + mr.mesh->corner_tris(), + mr.mesh->corner_tri_faces(), + mr.sharp_faces, + mr.mesh->vert_normals(), + mr.face_normals, + mr.corner_normals, + orco); + } + + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + GPU_vertformat_safe_attr_name("orco", attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + SNPRINTF(attr_name, "t%s", attr_safe_name); + GPU_vertformat_attr_add(format, attr_name, gpu_attr_type); + GPU_vertformat_alias_add(format, "t"); + GPU_vertformat_alias_add(format, "at"); + + return {std::move(tangents)}; + } + + Vector uv_names; for (int i = 0; i < MAX_MTFACE; i++) { if (tan_layers & (1 << i)) { char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; @@ -72,90 +105,45 @@ static void extract_tan_init_common(const MeshRenderData &mr, GPU_vertformat_alias_add(format, "at"); } - STRNCPY(r_tangent_names[tan_len++], layer_name); - } - } - if (use_orco_tan && orco.is_empty()) { - /* If `orco` is not available compute it ourselves */ - orco_allocated.reinitialize(mr.verts_num); - - if (mr.extract_type == MeshExtractType::BMesh) { - BMesh *bm = mr.bm; - for (int v = 0; v < mr.verts_num; v++) { - const BMVert *eve = BM_vert_at_index(bm, v); - /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords. - * not the distorted ones. */ - orco_allocated[v] = eve->co; - } - } - else { - for (int v = 0; v < mr.verts_num; v++) { - orco_allocated[v] = mr.vert_positions[v]; - } - } - /* TODO: This is not thread-safe. Draw extraction should not modify the mesh. */ - BKE_mesh_orco_verts_transform(const_cast(mr.mesh), orco_allocated, false); - orco = orco_allocated; - } - - /* Start Fresh */ - CustomData_reset(r_loop_data); - if (tan_len != 0 || use_orco_tan) { - short tangent_mask = 0; - bool calc_active_tangent = false; - if (mr.extract_type == MeshExtractType::BMesh) { - BKE_editmesh_loop_tangent_calc(mr.edit_bmesh, - calc_active_tangent, - r_tangent_names, - tan_len, - mr.bm_face_normals, - mr.bm_loop_normals, - orco, - r_loop_data, - mr.corners_num, - &tangent_mask); - } - else { - BKE_mesh_calc_loop_tangent_ex(mr.vert_positions, - mr.faces, - mr.corner_verts, - mr.mesh->corner_tris(), - mr.mesh->corner_tri_faces(), - mr.sharp_faces, - cd_ldata, - calc_active_tangent, - r_tangent_names, - tan_len, - mr.mesh->vert_normals(), - mr.face_normals, - mr.corner_normals, - orco, - r_loop_data, - mr.corner_verts.size(), - &tangent_mask); + uv_names.append(layer_name); } } - if (use_orco_tan) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(r_loop_data, CD_TANGENT, 0); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - SNPRINTF(attr_name, "t%s", attr_safe_name); - GPU_vertformat_attr_add(format, attr_name, gpu_attr_type); - GPU_vertformat_alias_add(format, "t"); - GPU_vertformat_alias_add(format, "at"); + if (uv_names.is_empty()) { + GPU_vertformat_attr_add(format, "dummy", blender::gpu::VertAttrType::SFLOAT_32); + return {}; + } + + Array> results; + if (mr.extract_type == MeshExtractType::BMesh) { + results = BKE_editmesh_uv_tangents_calc( + mr.edit_bmesh, mr.bm_face_normals, mr.bm_loop_normals, uv_names); + } + else { + Array> uv_maps(uv_names.size()); + Array> uv_map_spans(uv_names.size()); + const bke::AttributeAccessor attributes = mr.mesh->attributes(); + for (const int i : uv_names.index_range()) { + uv_maps[i] = *attributes.lookup(uv_names[i], bke::AttrDomain::Corner); + uv_map_spans[i] = uv_maps[i]; + } + results = bke::mesh::calc_uv_tangents(mr.vert_positions, + mr.faces, + mr.corner_verts, + mr.mesh->corner_tris(), + mr.mesh->corner_tri_faces(), + mr.sharp_faces, + mr.mesh->vert_normals(), + mr.face_normals, + mr.corner_normals, + uv_map_spans); } - int v_len = mr.corners_num; if (format->attr_len == 0) { GPU_vertformat_attr_add(format, "dummy", blender::gpu::VertAttrType::SFLOAT_32); - /* VBO will not be used, only allocate minimum of memory. */ - v_len = 1; } - *r_use_orco_tan = use_orco_tan; - *r_v_len = v_len; - *r_tan_len = tan_len; + return results; } gpu::VertBufPtr extract_tangents(const MeshRenderData &mr, @@ -166,41 +154,19 @@ gpu::VertBufPtr extract_tangents(const MeshRenderData &mr, gpu::VertAttrType::SNORM_10_10_10_2; GPUVertFormat format = {0}; - CustomData corner_data; - int v_len = 0; - int tan_len = 0; - bool use_orco_tan; - char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; - extract_tan_init_common(mr, - cache, - &format, - gpu_attr_type, - &corner_data, - &v_len, - &tan_len, - tangent_names, - &use_orco_tan); + const Array> tangents = extract_tan_init_common(mr, cache, &format, gpu_attr_type); + + const int vbo_size = tangents.size() * mr.corners_num; gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format)); - GPU_vertbuf_data_alloc(*vbo, v_len); + GPU_vertbuf_data_alloc(*vbo, vbo_size); if (use_hq) { short4 *tan_data = vbo->data().data(); - for (int i = 0; i < tan_len; i++) { - const char *name = tangent_names[i]; - const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_named( - &corner_data, CD_TANGENT, name); + for (const int i : tangents.index_range()) { + const Span layer_data = tangents[i]; for (int corner = 0; corner < mr.corners_num; corner++) { - *tan_data = gpu::convert_normal(layer_data[corner]); - (*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; - tan_data++; - } - } - if (use_orco_tan) { - const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_n( - &corner_data, CD_TANGENT, 0); - for (int corner = 0; corner < mr.corners_num; corner++) { - *tan_data = gpu::convert_normal(layer_data[corner]); + *tan_data = gpu::convert_normal(float3(layer_data[corner])); (*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? SHRT_MAX : SHRT_MIN; tan_data++; } @@ -208,28 +174,16 @@ gpu::VertBufPtr extract_tangents(const MeshRenderData &mr, } else { gpu::PackedNormal *tan_data = vbo->data().data(); - for (int i = 0; i < tan_len; i++) { - const char *name = tangent_names[i]; - const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_named( - &corner_data, CD_TANGENT, name); + for (const int i : tangents.index_range()) { + const Span layer_data = tangents[i]; for (int corner = 0; corner < mr.corners_num; corner++) { - *tan_data = gpu::convert_normal(layer_data[corner]); - tan_data->w = (layer_data[corner][3] > 0.0f) ? 1 : -2; - tan_data++; - } - } - if (use_orco_tan) { - const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_n( - &corner_data, CD_TANGENT, 0); - for (int corner = 0; corner < mr.corners_num; corner++) { - *tan_data = gpu::convert_normal(layer_data[corner]); + *tan_data = gpu::convert_normal(float3(layer_data[corner])); tan_data->w = (layer_data[corner][3] > 0.0f) ? 1 : -2; tan_data++; } } } - CustomData_free(&corner_data); return vbo; } @@ -246,20 +200,9 @@ gpu::VertBufPtr extract_tangents_subdiv(const MeshRenderData &mr, { gpu::VertAttrType gpu_attr_type = gpu::VertAttrType::SFLOAT_32_32_32_32; GPUVertFormat format = {0}; - CustomData corner_data; - int coarse_len = 0; - int tan_len = 0; - bool use_orco_tan; - char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; - extract_tan_init_common(mr, - cache, - &format, - gpu_attr_type, - &corner_data, - &coarse_len, - &tan_len, - tangent_names, - &use_orco_tan); + const Array> tangents = extract_tan_init_common(mr, cache, &format, gpu_attr_type); + + const int coarse_vbo_size = tangents.size() * mr.corners_num; gpu::VertBufPtr vbo = gpu::VertBufPtr( GPU_vertbuf_create_on_device(format, subdiv_cache.num_subdiv_loops)); @@ -267,35 +210,17 @@ gpu::VertBufPtr extract_tangents_subdiv(const MeshRenderData &mr, gpu::VertBuf *coarse_vbo = GPU_vertbuf_calloc(); /* Dynamic as we upload and interpolate layers one at a time. */ GPU_vertbuf_init_with_format_ex(*coarse_vbo, get_coarse_tan_format(), GPU_USAGE_DYNAMIC); - GPU_vertbuf_data_alloc(*coarse_vbo, coarse_len); + GPU_vertbuf_data_alloc(*coarse_vbo, coarse_vbo_size); /* Index of the tangent layer in the compact buffer. Used layers are stored in a single buffer. */ int pack_layer_index = 0; - for (int i = 0; i < tan_len; i++) { + for (const int i : tangents.index_range()) { float4 *tan_data = coarse_vbo->data().data(); - const char *name = tangent_names[i]; - const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_named( - &corner_data, CD_TANGENT, name); + const Span values = tangents[i]; for (int corner = 0; corner < mr.corners_num; corner++) { - *tan_data = layer_data[corner]; - (*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? 1.0f : -1.0f; - tan_data++; - } - - /* Ensure data is uploaded properly. */ - GPU_vertbuf_tag_dirty(coarse_vbo); - /* Include stride in offset. */ - const int dst_offset = int(subdiv_cache.num_subdiv_loops) * 4 * pack_layer_index++; - draw_subdiv_interp_custom_data(subdiv_cache, *coarse_vbo, *vbo, GPU_COMP_F32, 4, dst_offset); - } - if (use_orco_tan) { - float4 *tan_data = coarse_vbo->data().data(); - const float(*layer_data)[4] = (const float(*)[4])CustomData_get_layer_n( - &corner_data, CD_TANGENT, 0); - for (int corner = 0; corner < mr.corners_num; corner++) { - *tan_data = layer_data[corner]; - (*tan_data)[3] = (layer_data[corner][3] > 0.0f) ? 1.0f : -1.0f; + *tan_data = values[corner]; + (*tan_data)[3] = (values[corner][3] > 0.0f) ? 1.0f : -1.0f; tan_data++; } @@ -306,7 +231,6 @@ gpu::VertBufPtr extract_tangents_subdiv(const MeshRenderData &mr, draw_subdiv_interp_custom_data(subdiv_cache, *coarse_vbo, *vbo, GPU_COMP_F32, 4, dst_offset); } - CustomData_free(&corner_data); GPU_vertbuf_discard(coarse_vbo); return vbo; } diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 0187d1769a9..b3cf153faa5 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -120,6 +120,10 @@ typedef enum eCustomDataType { CD_MLOOPUV = 16, #endif CD_PROP_BYTE_COLOR = 17, + /** + * Previously used for runtime corner tangent storage in mesh #CustomData. Currently only used + * as an identifier to choose tangents in a few places. + */ CD_TANGENT = 18, CD_MDISPS = 19, CD_PROP_FLOAT4X4 = 20, @@ -193,7 +197,6 @@ using eCustomDataMask = uint64_t; #define CD_MASK_ORIGSPACE (1 << CD_ORIGSPACE) #define CD_MASK_ORCO (1 << CD_ORCO) #define CD_MASK_PROP_BYTE_COLOR (1 << CD_PROP_BYTE_COLOR) -#define CD_MASK_TANGENT (1 << CD_TANGENT) #define CD_MASK_MDISPS (1 << CD_MDISPS) #define CD_MASK_CLOTH_ORCO (1 << CD_CLOTH_ORCO) diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 3acc644ec13..aabf8f123e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -117,7 +117,6 @@ static void remove_unsupported_vert_data(Mesh &mesh) static void remove_unsupported_corner_data(Mesh &mesh) { CustomData_free_layers(&mesh.corner_data, CD_MDISPS); - CustomData_free_layers(&mesh.corner_data, CD_TANGENT); CustomData_free_layers(&mesh.corner_data, CD_MLOOPTANGENT); CustomData_free_layers(&mesh.corner_data, CD_GRID_PAINT_MASK); } diff --git a/source/blender/render/intern/bake.cc b/source/blender/render/intern/bake.cc index 14ab567fc4d..42f6a4f2ec7 100644 --- a/source/blender/render/intern/bake.cc +++ b/source/blender/render/intern/bake.cc @@ -88,18 +88,10 @@ struct BakeDataZSpan { float dv_dx, dv_dy; }; -/** - * struct wrapping up tangent space data - */ -struct TSpace { - float tangent[3]; - float sign; -}; - struct TriTessFace { const float *positions[3]; const float *vert_normals[3]; - const TSpace *tspace[3]; + blender::float4 tspace[3]; const float *loop_normal[3]; float normal[3]; /* for flat faces */ bool is_smooth; @@ -485,14 +477,23 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *mesh, bool tangent, Mesh *mesh_ blender::bke::mesh::corner_tris_calc(positions, faces, corner_verts, {corner_tris, tottri}); } - const TSpace *tspace = nullptr; + Array tspace; blender::Span corner_normals; if (tangent) { - BKE_mesh_calc_loop_tangents(mesh_eval, true, nullptr, 0); - - tspace = static_cast( - CustomData_get_layer(&mesh_eval->corner_data, CD_TANGENT)); - BLI_assert(tspace); + const StringRef active_uv_map = CustomData_get_active_layer_name(&mesh_eval->corner_data, + CD_PROP_FLOAT2); + const VArraySpan uv_map = *attributes.lookup(active_uv_map, bke::AttrDomain::Corner); + Array> result = bke::mesh::calc_uv_tangents(positions, + faces, + corner_verts, + {corner_tris, tottri}, + mesh->corner_tri_faces(), + VArraySpan(sharp_faces), + mesh->vert_normals(), + mesh->face_normals(), + mesh->corner_normals(), + {uv_map}); + tspace = std::move(result[0]); corner_normals = mesh_eval->corner_normals(); } @@ -512,9 +513,9 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *mesh, bool tangent, Mesh *mesh_ triangles[i].is_smooth = !sharp_faces[face_i]; if (tangent) { - triangles[i].tspace[0] = &tspace[tri[0]]; - triangles[i].tspace[1] = &tspace[tri[1]]; - triangles[i].tspace[2] = &tspace[tri[2]]; + triangles[i].tspace[0] = tspace[tri[0]]; + triangles[i].tspace[1] = tspace[tri[1]]; + triangles[i].tspace[2] = tspace[tri[2]]; } if (!corner_normals.is_empty()) { @@ -899,7 +900,7 @@ void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], is_smooth = triangle->is_smooth; for (j = 0; j < 3; j++) { - const TSpace *ts; + const blender::float4 *ts; if (is_smooth) { if (triangle->loop_normal[j]) { @@ -910,9 +911,9 @@ void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], } } - ts = triangle->tspace[j]; - copy_v3_v3(tangents[j], ts->tangent); - signs[j] = ts->sign; + ts = &triangle->tspace[j]; + copy_v3_v3(tangents[j], ts->xyz()); + signs[j] = ts->w; } u = pixel_array[i].uv[0]; diff --git a/source/blender/render/intern/multires_bake.cc b/source/blender/render/intern/multires_bake.cc index 0c06076900f..b6c9a547446 100644 --- a/source/blender/render/intern/multires_bake.cc +++ b/source/blender/render/intern/multires_bake.cc @@ -490,7 +490,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, reinterpret_cast(dm->getLoopDataArray(dm, CD_PROP_FLOAT2)), dm->getNumLoops(dm)); - float *pvtangent = nullptr; + Array pvtangent; ListBase threads; int i, tot_thread = bkr->threads > 0 ? bkr->threads : BLI_system_thread_count(); @@ -518,53 +518,42 @@ static void do_multires_bake(MultiresBakeRender *bkr, const Span tri_faces = temp_mesh->corner_tri_faces(); if (require_tangent) { - if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { - const bool *sharp_edges = static_cast( - CustomData_get_layer_named(&dm->edgeData, CD_PROP_BOOL, "sharp_edge")); - const bool *sharp_faces = static_cast( - CustomData_get_layer_named(&dm->polyData, CD_PROP_BOOL, "sharp_face")); + const bool *sharp_edges = static_cast( + CustomData_get_layer_named(&dm->edgeData, CD_PROP_BOOL, "sharp_edge")); + const bool *sharp_faces = static_cast( + CustomData_get_layer_named(&dm->polyData, CD_PROP_BOOL, "sharp_face")); - /* Copy sharp faces and edges, for corner normals domain and tangents - * to be computed correctly. */ - if (sharp_edges != nullptr) { - bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write(); - attributes.add("sharp_edge", - bke::AttrDomain::Edge, - bke::AttributeInitVArray(VArray::ForSpan( - Span(sharp_edges, temp_mesh->edges_num)))); - } - if (sharp_faces != nullptr) { - bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write(); - attributes.add("sharp_face", - bke::AttrDomain::Face, - bke::AttributeInitVArray(VArray::ForSpan( - Span(sharp_faces, temp_mesh->faces_num)))); - } - - const float3 *orco = static_cast(dm->getVertDataArray(dm, CD_ORCO)); - - const Span corner_normals = temp_mesh->corner_normals(); - BKE_mesh_calc_loop_tangent_ex(positions, - faces, - Span(dm->getCornerVertArray(dm), faces.total_size()), - corner_tris, - tri_faces, - sharp_faces ? Span(sharp_faces, faces.size()) : Span(), - &dm->loopData, - true, - nullptr, - 0, - vert_normals, - face_normals, - corner_normals, - orco ? Span(orco, positions.size()) : Span(), - /* result */ - &dm->loopData, - dm->getNumLoops(dm), - &dm->tangent_mask); + /* Copy sharp faces and edges, for corner normals domain and tangents + * to be computed correctly. */ + if (sharp_edges != nullptr) { + bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write(); + attributes.add("sharp_edge", + bke::AttrDomain::Edge, + bke::AttributeInitVArray(VArray::ForSpan( + Span(sharp_edges, temp_mesh->edges_num)))); + } + if (sharp_faces != nullptr) { + bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write(); + attributes.add("sharp_face", + bke::AttrDomain::Face, + bke::AttributeInitVArray(VArray::ForSpan( + Span(sharp_faces, temp_mesh->faces_num)))); } - pvtangent = static_cast(DM_get_loop_data_layer(dm, CD_TANGENT)); + const Span corner_normals = temp_mesh->corner_normals(); + Array> tangent_data = bke::mesh::calc_uv_tangents( + positions, + faces, + corner_verts, + corner_tris, + tri_faces, + sharp_faces ? Span(sharp_faces, faces.size()) : Span(), + vert_normals, + face_normals, + corner_normals, + {uv_map}); + + pvtangent = std::move(tangent_data[0]); } /* all threads shares the same custom bake data */ @@ -607,7 +596,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, CustomData_get_layer_named(&dm->polyData, CD_PROP_BOOL, "sharp_face")); handle->data.uv_map = uv_map; BKE_image_get_tile_uv(ima, tile->tile_number, handle->data.uv_offset); - handle->data.pvtangent = pvtangent; + handle->data.pvtangent = reinterpret_cast(pvtangent.data()); handle->data.w = ibuf->x; handle->data.h = ibuf->y; handle->data.hires_dm = bkr->hires_dm;