From b19696c0b8124d9ddfe229035f68ae841013c629 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 17 Jul 2025 02:24:09 +0200 Subject: [PATCH] Mesh: Simplify tangents calculation - Tangent calculation functions no longer use the CustomData struct as an input and output. - The "orco" and UV map calculations are exposed as separate API functions to avoid a confusing internal choice between the two - Redundancy in the API is removed, function names are clarified - Code is moved to C++ namespace - The "orco" case is clarified in the mesh draw tangent VBO extraction - CD_TANGENT layers are not stored in CustomData anymore, so some of that infrastructure is removed. - Broken logic for caching tangents in CustomData is removed. That hasn't worked for many years, if it every worked. We could investigate adding caching again later if that's helpful. Overall the change is motivated by the need to move away from CustomData for #122398. But the changes should be a general improvement that makes the code easier to understand either way. Testing for this PR included using the default render UV in materials, referencing specific UV tangents by name, using the spherical position- based tangents in a material, and baking textures (multires and normal baking). Pull Request: https://projects.blender.org/blender/blender/pulls/141799 --- .../blenkernel/BKE_editmesh_tangent.hh | 29 +- .../BKE_mesh_legacy_derived_mesh.hh | 2 - source/blender/blenkernel/BKE_mesh_runtime.hh | 2 - source/blender/blenkernel/BKE_mesh_tangent.hh | 75 +-- .../intern/attribute_legacy_convert.cc | 2 +- .../blender/blenkernel/intern/customdata.cc | 17 +- .../blenkernel/intern/editmesh_tangent.cc | 299 +++++------- .../blenkernel/intern/mesh_flip_faces.cc | 1 - .../blenkernel/intern/mesh_legacy_convert.cc | 23 - .../blender/blenkernel/intern/mesh_tangent.cc | 443 ++++++------------ .../mesh_extractors/extract_mesh_vbo_tan.cc | 270 ++++------- .../blender/makesdna/DNA_customdata_types.h | 5 +- .../geometry/nodes/node_geo_extrude_mesh.cc | 1 - source/blender/render/intern/bake.cc | 45 +- source/blender/render/intern/multires_bake.cc | 81 ++-- 15 files changed, 462 insertions(+), 833 deletions(-) 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;