Blender V5.0
editmesh_tangent.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_geom.h"
10#include "BLI_math_vector.h"
11#include "BLI_task.hh"
12
14
15#include "BKE_customdata.hh"
16#include "BKE_editmesh.hh"
18#include "BKE_mesh.hh"
19
20#include "MEM_guardedalloc.h"
21
22/* interface */
23#include "mikktspace.hh"
24
25using blender::Array;
26using blender::float3;
27using blender::float4;
28using blender::Span;
30
31/* -------------------------------------------------------------------- */
34
35/* Necessary complexity to handle looptris as quads for correct tangents. */
36#define USE_LOOPTRI_DETECT_QUADS
37
40 {
41#ifdef USE_LOOPTRI_DETECT_QUADS
43#else
44 return uint(numTessFaces);
45#endif
46 }
47
49 {
50#ifdef USE_LOOPTRI_DETECT_QUADS
51 if (face_as_quad_map) {
52 if (looptris[face_as_quad_map[face_num]][0]->f->len == 4) {
53 return 4;
54 }
55 }
56 return 3;
57#else
58 UNUSED_VARS(pContext, face_num);
59 return 3;
60#endif
61 }
62
63 const BMLoop *GetLoop(const uint face_num, uint vert_index)
64 {
65 // BLI_assert(vert_index >= 0 && vert_index < 4);
66 BMLoop *const *ltri;
67 const BMLoop *l;
68
69#ifdef USE_LOOPTRI_DETECT_QUADS
70 if (face_as_quad_map) {
71 ltri = looptris[face_as_quad_map[face_num]].data();
72 if (ltri[0]->f->len == 4) {
73 l = BM_FACE_FIRST_LOOP(ltri[0]->f);
74 while (vert_index--) {
75 l = l->next;
76 }
77 return l;
78 }
79 /* fall through to regular triangle */
80 }
81 else {
82 ltri = looptris[face_num].data();
83 }
84#else
85 ltri = looptris[face_num].data();
86#endif
87 return ltri[vert_index];
88 }
89
90 mikk::float3 GetPosition(const uint face_num, const uint vert_index)
91 {
92 const BMLoop *l = GetLoop(face_num, vert_index);
93 return mikk::float3(l->v->co);
94 }
95
96 mikk::float3 GetTexCoord(const uint face_num, const uint vert_index)
97 {
98 const BMLoop *l = GetLoop(face_num, vert_index);
99 if (has_uv()) {
100 const float *uv = (const float *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
101 return mikk::float3(uv[0], uv[1], 1.0f);
102 }
103 const float *orco_p = orco[BM_elem_index_get(l->v)];
104 float u, v;
105 map_to_sphere(&u, &v, orco_p[0], orco_p[1], orco_p[2]);
106 return mikk::float3(u, v, 1.0f);
107 }
108
109 mikk::float3 GetNormal(const uint face_num, const uint vert_index)
110 {
111 const BMLoop *l = GetLoop(face_num, vert_index);
112 if (!corner_normals.is_empty()) {
114 }
115 if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH) == 0) { /* flat */
116 if (!face_normals.is_empty()) {
118 }
119 return mikk::float3(l->f->no);
120 }
121 return mikk::float3(l->v->no);
122 }
123
124 void SetTangentSpace(const uint face_num,
125 const uint vert_index,
127 bool orientation)
128 {
129 const BMLoop *l = GetLoop(face_num, vert_index);
130 float *p_res = tangent[BM_elem_index_get(l)];
131 copy_v4_fl4(p_res, T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
132 }
133
134 bool has_uv()
135 {
136 return cd_loop_uv_offset != -1;
137 }
138
142 int cd_loop_uv_offset; /* texture coordinates */
144 float (*tangent)[4]; /* destination */
146
147#ifdef USE_LOOPTRI_DETECT_QUADS
148 /* map from 'fake' face index to looptris,
149 * quads will point to the first looptri of the quad */
152#endif
153};
154
156 BMEditMesh *&em, BMesh *&bm, int &totface, int &num_face_as_quad_map, int *&face_as_quad_map)
157{
158#ifdef USE_LOOPTRI_DETECT_QUADS
159 if (em->looptris.size() != bm->totface) {
160 /* Over allocate, since we don't know how many ngon or quads we have. */
161
162 /* map fake face index to looptri */
163 face_as_quad_map = MEM_malloc_arrayN<int>(size_t(totface), __func__);
164 int i, j;
165 for (i = 0, j = 0; j < totface; i++, j++) {
166 face_as_quad_map[i] = j;
167 /* step over all quads */
168 if (em->looptris[j][0]->f->len == 4) {
169 j++; /* Skips the next looptri. */
170 }
171 }
172 num_face_as_quad_map = i;
173 }
174 else {
175 num_face_as_quad_map = totface;
176 }
177#else
178 num_face_as_quad_map = totface;
179 face_as_quad_map = nullptr;
180#endif
181}
182
184 const Span<float3> face_normals,
185 const Span<float3> corner_normals,
186 const Span<StringRef> uv_names)
187{
188 using namespace blender;
189 if (em->looptris.is_empty()) {
190 return {};
191 }
192
193 BMesh *bm = em->bm;
194
195 int totface = em->looptris.size();
196 int num_face_as_quad_map;
197 int *face_as_quad_map = nullptr;
198 calc_face_as_quad_map(em, bm, totface, num_face_as_quad_map, face_as_quad_map);
199
200 Array<Array<float4>> result(uv_names.size());
201
202 /* needed for indexing loop-tangents */
203 int htype_index = BM_LOOP;
204 if (!face_normals.is_empty()) {
205 /* needed for face normal lookups */
206 htype_index |= BM_FACE;
207 }
208 BM_mesh_elem_index_ensure(bm, htype_index);
209
210 threading::parallel_for(uv_names.index_range(), 1, [&](const IndexRange range) {
211 for (const int n : range) {
212 SGLSLEditMeshToTangent mesh2tangent{};
213 mesh2tangent.numTessFaces = em->looptris.size();
214 mesh2tangent.face_as_quad_map = face_as_quad_map;
215 mesh2tangent.num_face_as_quad_map = num_face_as_quad_map;
216 mesh2tangent.face_normals = face_normals;
217 /* NOTE: we assume we do have tessellated loop normals at this point
218 * (in case it is object-enabled), have to check this is valid. */
219 mesh2tangent.corner_normals = corner_normals;
220 mesh2tangent.cd_loop_uv_offset = CustomData_get_offset_named(
221 &bm->ldata, CD_PROP_FLOAT2, uv_names[n]);
222 BLI_assert(mesh2tangent.cd_loop_uv_offset != -1);
223
224 mesh2tangent.looptris = em->looptris;
225 result[n].reinitialize(bm->totloop);
226 mesh2tangent.tangent = reinterpret_cast<float (*)[4]>(result[n].data());
227
228 mikk::Mikktspace<SGLSLEditMeshToTangent> mikk(mesh2tangent);
229 mikk.genTangSpace();
230 }
231 });
232
233 MEM_SAFE_FREE(face_as_quad_map);
234
235 return result;
236}
237
239 const Span<float3> face_normals,
240 const Span<float3> corner_normals,
241 const Span<float3> vert_orco)
242{
243 if (em->looptris.is_empty()) {
244 return {};
245 }
246
247 BMesh *bm = em->bm;
248
249 int totface = em->looptris.size();
250 int num_face_as_quad_map;
251 int *face_as_quad_map = nullptr;
252 calc_face_as_quad_map(em, bm, totface, num_face_as_quad_map, face_as_quad_map);
253
254 Array<float4> result(bm->totloop);
255
256 /* needed for indexing loop-tangents */
257 int htype_index = BM_LOOP;
258 /* needed for orco lookups */
259 htype_index |= BM_VERT;
260 if (!face_normals.is_empty()) {
261 /* needed for face normal lookups */
262 htype_index |= BM_FACE;
263 }
264 BM_mesh_elem_index_ensure(bm, htype_index);
265
266 SGLSLEditMeshToTangent mesh2tangent{};
267 mesh2tangent.numTessFaces = em->looptris.size();
268 mesh2tangent.face_as_quad_map = face_as_quad_map;
269 mesh2tangent.num_face_as_quad_map = num_face_as_quad_map;
270 mesh2tangent.face_normals = face_normals;
271 /* NOTE: we assume we do have tessellated loop normals at this point
272 * (in case it is object-enabled), have to check this is valid. */
273 mesh2tangent.corner_normals = corner_normals;
274 mesh2tangent.cd_loop_uv_offset = -1;
275 mesh2tangent.orco = vert_orco;
276
277 mesh2tangent.looptris = em->looptris;
278 mesh2tangent.tangent = reinterpret_cast<float (*)[4]>(result.data());
280 mikk.genTangSpace();
281
282 MEM_SAFE_FREE(face_as_quad_map);
283
284 return result;
285}
286
CustomData interface, see also DNA_customdata_types.h.
bool map_to_sphere(float *r_u, float *r_v, float x, float y, float z)
MINLINE void copy_v4_fl4(float v[4], float x, float y, float z, float w)
unsigned int uint
#define UNUSED_VARS(...)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_SMOOTH
@ BM_LOOP
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
#define BM_elem_index_get(ele)
#define BM_elem_flag_test(ele, hflag)
BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_VERT
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
int64_t size() const
Definition BLI_array.hh:256
bool is_empty() const
Definition BLI_array.hh:264
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
nullptr float
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)
Array< float4 > BKE_editmesh_orco_tangents_calc(BMEditMesh *em, const Span< float3 > face_normals, const Span< float3 > corner_normals, const Span< float3 > vert_orco)
static void calc_face_as_quad_map(BMEditMesh *&em, BMesh *&bm, int &totface, int &num_face_as_quad_map, int *&face_as_quad_map)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
#define T
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< float, 4 > float4
VecBase< float, 3 > float3
blender::Array< std::array< BMLoop *, 3 > > looptris
mikk::float3 GetNormal(const uint face_num, const uint vert_index)
mikk::float3 GetPosition(const uint face_num, const uint vert_index)
mikk::float3 GetTexCoord(const uint face_num, const uint vert_index)
const BMLoop * GetLoop(const uint face_num, uint vert_index)
Span< std::array< BMLoop *, 3 > > looptris
uint GetNumVerticesOfFace(const uint face_num)
void SetTangentSpace(const uint face_num, const uint vert_index, mikk::float3 T, bool orientation)
i
Definition text_draw.cc:230