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
This commit is contained in:
@@ -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<blender::float3> face_normals,
|
||||
blender::Span<blender::float3> corner_normals,
|
||||
blender::Span<blender::float3> vert_orco,
|
||||
CustomData *dm_loopdata_out,
|
||||
uint dm_loopdata_out_len,
|
||||
short *tangent_mask_curr_p);
|
||||
blender::Array<blender::Array<blender::float4>> BKE_editmesh_uv_tangents_calc(
|
||||
BMEditMesh *em,
|
||||
blender::Span<blender::float3> face_normals,
|
||||
blender::Span<blender::float3> corner_normals,
|
||||
blender::Span<blender::StringRef> uv_names);
|
||||
|
||||
blender::Array<blender::float4> BKE_editmesh_orco_tangents_calc(
|
||||
BMEditMesh *em,
|
||||
blender::Span<blender::float3> face_normals,
|
||||
blender::Span<blender::float3> corner_normals,
|
||||
blender::Span<blender::float3> vert_orco);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<blender::float3> vert_positions,
|
||||
blender::OffsetIndices<int> faces,
|
||||
blender::Span<int> corner_verts,
|
||||
blender::Span<blender::int3> corner_tris,
|
||||
blender::Span<int> corner_tri_faces,
|
||||
blender::Span<bool> sharp_faces,
|
||||
const CustomData *loopdata,
|
||||
bool calc_active_tangent,
|
||||
const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME],
|
||||
int tangent_names_len,
|
||||
blender::Span<blender::float3> vert_normals,
|
||||
blender::Span<blender::float3> face_normals,
|
||||
blender::Span<blender::float3> corner_normals,
|
||||
blender::Span<blender::float3> vert_orco,
|
||||
/* result */
|
||||
CustomData *loopdata_out,
|
||||
uint loopdata_out_len,
|
||||
short *tangent_mask_curr_p);
|
||||
Array<Array<float4>> calc_uv_tangents(Span<float3> vert_positions,
|
||||
OffsetIndices<int> faces,
|
||||
Span<int> corner_verts,
|
||||
Span<int3> corner_tris,
|
||||
Span<int> corner_tri_faces,
|
||||
Span<bool> sharp_faces,
|
||||
Span<float3> vert_normals,
|
||||
Span<float3> face_normals,
|
||||
Span<float3> corner_normals,
|
||||
Span<Span<float2>> 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<float4> calc_orco_tangents(Span<float3> vert_positions,
|
||||
OffsetIndices<int> faces,
|
||||
Span<int> corner_verts,
|
||||
Span<int3> corner_tris,
|
||||
Span<int> corner_tri_faces,
|
||||
Span<bool> sharp_faces,
|
||||
Span<float3> vert_normals,
|
||||
Span<float3> face_normals,
|
||||
Span<float3> corner_normals,
|
||||
Span<float3> 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
|
||||
|
||||
@@ -23,6 +23,7 @@ std::optional<AttrType> 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<AttrType> 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:
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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<SGLSLEditMeshToTangent *>(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<SGLSLEditMeshToTangent> mikk(*mesh_data);
|
||||
mikk.genTangSpace();
|
||||
/* map fake face index to looptri */
|
||||
face_as_quad_map = MEM_malloc_arrayN<int>(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<float3> face_normals,
|
||||
const Span<float3> corner_normals,
|
||||
const Span<float3> vert_orco,
|
||||
/* result */
|
||||
CustomData *loopdata_out,
|
||||
const uint loopdata_out_len,
|
||||
short *tangent_mask_curr_p)
|
||||
Array<Array<float4>> BKE_editmesh_uv_tangents_calc(BMEditMesh *em,
|
||||
const Span<float3> face_normals,
|
||||
const Span<float3> corner_normals,
|
||||
const Span<StringRef> 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<int>(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<float(*)[4]>(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<Array<float4>> 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<float(*)[4]>(result[n].data());
|
||||
|
||||
mikk::Mikktspace<SGLSLEditMeshToTangent> mikk(mesh2tangent);
|
||||
mikk.genTangSpace();
|
||||
}
|
||||
});
|
||||
|
||||
MEM_SAFE_FREE(face_as_quad_map);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Array<float4> BKE_editmesh_orco_tangents_calc(BMEditMesh *em,
|
||||
const Span<float3> face_normals,
|
||||
const Span<float3> corner_normals,
|
||||
const Span<float3> 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<float4> 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<float(*)[4]>(result.data());
|
||||
mikk::Mikktspace<SGLSLEditMeshToTangent> mikk(mesh2tangent);
|
||||
mikk.genTangSpace();
|
||||
|
||||
MEM_SAFE_FREE(face_as_quad_map);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -57,7 +57,6 @@ void mesh_flip_faces(Mesh &mesh, const IndexMask &selection)
|
||||
}
|
||||
});
|
||||
|
||||
flip_custom_data_type<float4>(faces, mesh.corner_data, selection, CD_TANGENT);
|
||||
flip_custom_data_type<float4>(faces, mesh.corner_data, selection, CD_MLOOPTANGENT);
|
||||
flip_custom_data_type<GridPaintMask>(faces, mesh.corner_data, selection, CD_GRID_PAINT_MASK);
|
||||
flip_custom_data_type<OrigSpaceLoop>(faces, mesh.corner_data, selection, CD_ORIGSPACE_MLOOP);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<float3> face_normals;
|
||||
Span<float3> corner_normals;
|
||||
const int3 *corner_tris;
|
||||
const int *tri_faces;
|
||||
const float2 *mloopuv; /* texture coordinates */
|
||||
Span<float2> mloopuv; /* texture coordinates */
|
||||
OffsetIndices<int> faces;
|
||||
const int *corner_verts; /* indices */
|
||||
Span<float3> 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<int> &faces,
|
||||
const Span<int3> &corner_tris,
|
||||
const Span<int> &corner_tri_faces,
|
||||
int &num_face_as_quad_map,
|
||||
int *&face_as_quad_map)
|
||||
{
|
||||
SGLSLMeshToTangent *mesh_data = static_cast<SGLSLMeshToTangent *>(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<SGLSLMeshToTangent> mikk(*mesh_data);
|
||||
/* Map fake face index to corner_tris. */
|
||||
face_as_quad_map = MEM_malloc_arrayN<int>(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<Array<float4>> calc_uv_tangents(const Span<float3> vert_positions,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const Span<int3> corner_tris,
|
||||
const Span<int> corner_tri_faces,
|
||||
const Span<bool> sharp_faces,
|
||||
const Span<float3> vert_normals,
|
||||
const Span<float3> face_normals,
|
||||
const Span<float3> corner_normals,
|
||||
const Span<Span<float2>> 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<Array<float4>> 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<float(*)[4]>(results[i].data());
|
||||
|
||||
mikk::Mikktspace<SGLSLMeshToTangent> mikk(mesh2tangent);
|
||||
mikk.genTangSpace();
|
||||
}
|
||||
});
|
||||
|
||||
MEM_SAFE_FREE(face_as_quad_map);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
Array<float4> calc_orco_tangents(const Span<float3> vert_positions,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const Span<int3> corner_tris,
|
||||
const Span<int> corner_tri_faces,
|
||||
const Span<bool> sharp_faces,
|
||||
const Span<float3> vert_normals,
|
||||
const Span<float3> face_normals,
|
||||
const Span<float3> corner_normals,
|
||||
const Span<float3> 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<float4> 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<float(*)[4]>(results.data());
|
||||
|
||||
mikk::Mikktspace<SGLSLMeshToTangent> 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<float3> vert_positions,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_verts,
|
||||
const Span<int3> corner_tris,
|
||||
const Span<int> corner_tri_faces,
|
||||
const Span<bool> sharp_faces,
|
||||
const CustomData *loopdata,
|
||||
bool calc_active_tangent,
|
||||
const char (*tangent_names)[MAX_CUSTOMDATA_LAYER_NAME],
|
||||
int tangent_names_len,
|
||||
const Span<float3> vert_normals,
|
||||
const Span<float3> face_normals,
|
||||
const Span<float3> corner_normals,
|
||||
const Span<float3> 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<int>(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<const float2 *>(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<float(*)[4]>(loopdata_out->layers[index].data);
|
||||
BLI_task_pool_push(task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, nullptr);
|
||||
}
|
||||
|
||||
BLI_assert(tangent_mask_curr == tangent_mask);
|
||||
BLI_task_pool_work_and_wait(task_pool);
|
||||
BLI_task_pool_free(task_pool);
|
||||
}
|
||||
else {
|
||||
tangent_mask_curr = tangent_mask;
|
||||
}
|
||||
#ifdef USE_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<int3> corner_tris = mesh_eval->corner_tris();
|
||||
const bke::AttributeAccessor attributes = mesh_eval->attributes();
|
||||
const VArraySpan sharp_face = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
|
||||
const float3 *orco = static_cast<const float3 *>(
|
||||
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<float3>(),
|
||||
/* result */
|
||||
&mesh_eval->corner_data,
|
||||
uint(mesh_eval->corners_num),
|
||||
&tangent_mask);
|
||||
}
|
||||
} // namespace blender::bke::mesh
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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<Array<float4>> 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<const float3 *>(CustomData_get_layer(cd_vdata, CD_ORCO));
|
||||
Span<float3> orco = orco_ptr ? Span(orco_ptr, mr.verts_num) : Span<float3>();
|
||||
Array<float3> 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<float4> tangents;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
Array<float3> 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<float3> orco;
|
||||
Array<float3> orco_allocated;
|
||||
if (const float3 *orco_ptr = static_cast<const float3 *>(
|
||||
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<Mesh *>(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<StringRef> 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<Mesh *>(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<Array<float4>> 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<VArraySpan<float2>> uv_maps(uv_names.size());
|
||||
Array<Span<float2>> 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<float2>(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<Array<float4>> 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<short4>().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<float4> layer_data = tangents[i];
|
||||
for (int corner = 0; corner < mr.corners_num; corner++) {
|
||||
*tan_data = gpu::convert_normal<short4>(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<short4>(layer_data[corner]);
|
||||
*tan_data = gpu::convert_normal<short4>(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<gpu::PackedNormal>().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<float4> layer_data = tangents[i];
|
||||
for (int corner = 0; corner < mr.corners_num; corner++) {
|
||||
*tan_data = gpu::convert_normal<gpu::PackedNormal>(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<gpu::PackedNormal>(layer_data[corner]);
|
||||
*tan_data = gpu::convert_normal<gpu::PackedNormal>(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<Array<float4>> 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<float4>().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<float4> 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<float4>().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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<float4> tspace;
|
||||
blender::Span<blender::float3> corner_normals;
|
||||
if (tangent) {
|
||||
BKE_mesh_calc_loop_tangents(mesh_eval, true, nullptr, 0);
|
||||
|
||||
tspace = static_cast<const TSpace *>(
|
||||
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<float2>(active_uv_map, bke::AttrDomain::Corner);
|
||||
Array<Array<float4>> 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];
|
||||
|
||||
@@ -490,7 +490,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
|
||||
reinterpret_cast<const float2 *>(dm->getLoopDataArray(dm, CD_PROP_FLOAT2)),
|
||||
dm->getNumLoops(dm));
|
||||
|
||||
float *pvtangent = nullptr;
|
||||
Array<blender::float4> 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<int> 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<const bool *>(
|
||||
CustomData_get_layer_named(&dm->edgeData, CD_PROP_BOOL, "sharp_edge"));
|
||||
const bool *sharp_faces = static_cast<const bool *>(
|
||||
CustomData_get_layer_named(&dm->polyData, CD_PROP_BOOL, "sharp_face"));
|
||||
const bool *sharp_edges = static_cast<const bool *>(
|
||||
CustomData_get_layer_named(&dm->edgeData, CD_PROP_BOOL, "sharp_edge"));
|
||||
const bool *sharp_faces = static_cast<const bool *>(
|
||||
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<bool>("sharp_edge",
|
||||
bke::AttrDomain::Edge,
|
||||
bke::AttributeInitVArray(VArray<bool>::ForSpan(
|
||||
Span<bool>(sharp_edges, temp_mesh->edges_num))));
|
||||
}
|
||||
if (sharp_faces != nullptr) {
|
||||
bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write();
|
||||
attributes.add<bool>("sharp_face",
|
||||
bke::AttrDomain::Face,
|
||||
bke::AttributeInitVArray(VArray<bool>::ForSpan(
|
||||
Span<bool>(sharp_faces, temp_mesh->faces_num))));
|
||||
}
|
||||
|
||||
const float3 *orco = static_cast<const float3 *>(dm->getVertDataArray(dm, CD_ORCO));
|
||||
|
||||
const Span<float3> 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<bool>(),
|
||||
&dm->loopData,
|
||||
true,
|
||||
nullptr,
|
||||
0,
|
||||
vert_normals,
|
||||
face_normals,
|
||||
corner_normals,
|
||||
orco ? Span(orco, positions.size()) : Span<float3>(),
|
||||
/* 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<bool>("sharp_edge",
|
||||
bke::AttrDomain::Edge,
|
||||
bke::AttributeInitVArray(VArray<bool>::ForSpan(
|
||||
Span<bool>(sharp_edges, temp_mesh->edges_num))));
|
||||
}
|
||||
if (sharp_faces != nullptr) {
|
||||
bke::MutableAttributeAccessor attributes = temp_mesh->attributes_for_write();
|
||||
attributes.add<bool>("sharp_face",
|
||||
bke::AttrDomain::Face,
|
||||
bke::AttributeInitVArray(VArray<bool>::ForSpan(
|
||||
Span<bool>(sharp_faces, temp_mesh->faces_num))));
|
||||
}
|
||||
|
||||
pvtangent = static_cast<float *>(DM_get_loop_data_layer(dm, CD_TANGENT));
|
||||
const Span<float3> corner_normals = temp_mesh->corner_normals();
|
||||
Array<Array<float4>> tangent_data = bke::mesh::calc_uv_tangents(
|
||||
positions,
|
||||
faces,
|
||||
corner_verts,
|
||||
corner_tris,
|
||||
tri_faces,
|
||||
sharp_faces ? Span(sharp_faces, faces.size()) : Span<bool>(),
|
||||
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<float *>(pvtangent.data());
|
||||
handle->data.w = ibuf->x;
|
||||
handle->data.h = ibuf->y;
|
||||
handle->data.hires_dm = bkr->hires_dm;
|
||||
|
||||
Reference in New Issue
Block a user