Blender V5.0
mesh_tangent.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_math_geom.h"
14#include "BLI_math_vector.h"
15#include "BLI_task.hh"
16
17#include "BKE_mesh_tangent.hh"
18#include "BKE_report.hh"
19
20#include "mikktspace.hh"
21
22#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
23
24/* -------------------------------------------------------------------- */
27
28namespace blender::bke::mesh {
29
37
39 {
40 return uint(this->faces.size());
41 }
42
44 {
45 return uint(this->faces[face_num].size());
46 }
47
48 mikk::float3 GetPosition(const uint face_num, const uint vert_num)
49 {
50 const uint loop_idx = uint(this->faces[face_num].start()) + vert_num;
51 return mikk::float3(this->positions[this->corner_verts[loop_idx]]);
52 }
53
54 mikk::float3 GetTexCoord(const uint face_num, const uint vert_num)
55 {
56 const float *uv = this->uv_map[uint(this->faces[face_num].start()) + vert_num];
57 return mikk::float3(uv[0], uv[1], 1.0f);
58 }
59
60 mikk::float3 GetNormal(const uint face_num, const uint vert_num)
61 {
62 return mikk::float3(this->corner_normals[uint(this->faces[face_num].start()) + vert_num]);
63 }
64
65 void SetTangentSpace(const uint face_num, const uint vert_num, mikk::float3 T, bool orientation)
66 {
67 float *p_res = this->tangents[uint(this->faces[face_num].start()) + vert_num];
68 copy_v4_fl4(p_res, T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
69 }
70
71 bool has_uv() const
72 {
73 return true;
74 }
75};
76
77void calc_uv_tangent_tris_quads(const Span<float3> vert_positions,
79 const Span<int> corner_verts,
80 const Span<float3> corner_normals,
81 const Span<float2> uv_map,
82 MutableSpan<float4> results,
83 ReportList *reports)
84{
85 MeshToTangentQuadsTris mesh_to_tangent;
86 mesh_to_tangent.positions = vert_positions;
87 mesh_to_tangent.faces = faces;
88 mesh_to_tangent.corner_verts = corner_verts;
89 mesh_to_tangent.corner_normals = corner_normals;
90 mesh_to_tangent.uv_map = uv_map;
91 mesh_to_tangent.tangents = results;
92
94
95 /* First check we do have a tris/quads only mesh. */
96 for (const int64_t i : faces.index_range()) {
97 if (faces[i].size() > 4) {
99 reports, RPT_ERROR, "Tangent space can only be computed for tris/quads, aborting");
100 return;
101 }
102 }
103
104 mikk.genTangSpace();
105}
106
108
109/* -------------------------------------------------------------------- */
112
113/* Necessary complexity to handle corner_tris as quads for correct tangents. */
114#define USE_TRI_DETECT_QUADS
115
129
130#ifdef USE_TRI_DETECT_QUADS
131 /* map from 'fake' face index to corner_tris,
132 * quads will point to the first corner_tris of the quad */
135#endif
136
138 {
139#ifdef USE_TRI_DETECT_QUADS
140 return uint(this->num_face_as_quad_map);
141#else
142 return uint(this->corner_tris.size());
143#endif
144 }
145
147 {
148#ifdef USE_TRI_DETECT_QUADS
149 if (this->face_as_quad_map) {
150 const int face_index = this->tri_faces[this->face_as_quad_map[face_num]];
151 if (this->faces[face_index].size() == 4) {
152 return 4;
153 }
154 }
155 return 3;
156#else
157 UNUSED_VARS(pContext, face_num);
158 return 3;
159#endif
160 }
161
162 uint GetLoop(const uint face_num, const uint vert_num, int3 &tri, int &face_index)
163 {
164#ifdef USE_TRI_DETECT_QUADS
165 if (face_as_quad_map) {
166 tri = this->corner_tris[face_as_quad_map[face_num]];
167 face_index = this->tri_faces[face_as_quad_map[face_num]];
168 if (this->faces[face_index].size() == 4) {
169 return uint(this->faces[face_index][vert_num]);
170 }
171 /* fall through to regular triangle */
172 }
173 else {
174 tri = this->corner_tris[face_num];
175 face_index = this->tri_faces[face_num];
176 }
177#else
178 tri = &this->corner_tris[face_num];
179#endif
180
181 /* Safe to suppress since the way `face_as_quad_map` is used
182 * prevents out-of-bounds reads on the 4th component of the `int3`. */
183#ifdef __GNUC__
184# pragma GCC diagnostic push
185# pragma GCC diagnostic ignored "-Warray-bounds"
186#endif
187
188 return uint(tri[int(vert_num)]);
189
190#ifdef __GNUC__
191# pragma GCC diagnostic pop
192#endif
193 }
194
195 mikk::float3 GetPosition(const uint face_num, const uint vert_num)
196 {
197 int3 tri;
198 int face_index;
199 uint loop_index = this->GetLoop(face_num, vert_num, tri, face_index);
200 return mikk::float3(this->positions[this->corner_verts[loop_index]]);
201 }
202
203 mikk::float3 GetTexCoord(const uint face_num, const uint vert_num)
204 {
205 int3 tri;
206 int face_index;
207 uint loop_index = this->GetLoop(face_num, vert_num, tri, face_index);
208 if (has_uv()) {
209 const float2 &uv = this->uv_map[loop_index];
210 return mikk::float3(uv[0], uv[1], 1.0f);
211 }
212 const float *l_orco = this->orco[this->corner_verts[loop_index]];
213 float u, v;
214 map_to_sphere(&u, &v, l_orco[0], l_orco[1], l_orco[2]);
215 return mikk::float3(u, v, 1.0f);
216 }
217
218 mikk::float3 GetNormal(const uint face_num, const uint vert_num)
219 {
220 int3 tri;
221 int face_index;
222 uint loop_index = this->GetLoop(face_num, vert_num, tri, face_index);
223 if (!this->corner_normals.is_empty()) {
224 return mikk::float3(this->corner_normals[loop_index]);
225 }
226 if (!this->sharp_faces.is_empty() && this->sharp_faces[face_index]) { /* flat */
227 if (!this->face_normals.is_empty()) {
228 return mikk::float3(this->face_normals[face_index]);
229 }
230#ifdef USE_TRI_DETECT_QUADS
231 const blender::IndexRange face = this->faces[face_index];
232 float normal[3];
233 if (face.size() == 4) {
234 normal_quad_v3(normal,
235 this->positions[this->corner_verts[face[0]]],
236 this->positions[this->corner_verts[face[1]]],
237 this->positions[this->corner_verts[face[2]]],
238 this->positions[this->corner_verts[face[3]]]);
239 }
240 else
241#endif
242 {
243 normal_tri_v3(normal,
244 this->positions[this->corner_verts[tri[0]]],
245 this->positions[this->corner_verts[tri[1]]],
246 this->positions[this->corner_verts[tri[2]]]);
247 }
248 return mikk::float3(normal);
249 }
250 return mikk::float3(this->vert_normals[this->corner_verts[loop_index]]);
251 }
252
253 void SetTangentSpace(const uint face_num, const uint vert_num, mikk::float3 T, bool orientation)
254 {
255 int3 tri;
256 int face_index;
257 uint loop_index = this->GetLoop(face_num, vert_num, tri, face_index);
258 copy_v4_fl4(this->tangents[loop_index], T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
259 }
260
261 bool has_uv() const
262 {
263 return !this->uv_map.is_empty();
264 }
265};
266
268 const Span<int3> corner_tris,
269 const Span<int> corner_tri_faces,
270 int &num_face_as_quad_map,
271 int *&face_as_quad_map)
272{
273#ifdef USE_TRI_DETECT_QUADS
274 if (corner_tris.size() != faces.size()) {
275 /* Over allocate, since we don't know how many ngon or quads we have. */
276
277 /* Map fake face index to corner_tris. */
278 face_as_quad_map = MEM_malloc_arrayN<int>(size_t(corner_tris.size()), __func__);
279 int k, j;
280 for (k = 0, j = 0; j < int(corner_tris.size()); k++, j++) {
281 face_as_quad_map[k] = j;
282 /* step over all quads */
283 if (faces[corner_tri_faces[j]].size() == 4) {
284 j++; /* Skips the next corner_tri. */
285 }
286 }
287 num_face_as_quad_map = k;
288 }
289 else {
290 num_face_as_quad_map = int(corner_tris.size());
291 }
292#else
293 num_face_as_quad_map = 0;
294 face_as_quad_map = nullptr;
295#endif
296}
297
300 const Span<int> corner_verts,
301 const Span<int3> corner_tris,
302 const Span<int> corner_tri_faces,
303 const Span<bool> sharp_faces,
304 const Span<float3> vert_normals,
305 const Span<float3> face_normals,
306 const Span<float3> corner_normals,
307 const Span<Span<float2>> uv_maps)
308{
309 if (corner_tris.is_empty()) {
310 return {};
311 }
312
313 int num_face_as_quad_map;
314 int *face_as_quad_map = nullptr;
316 faces, corner_tris, corner_tri_faces, num_face_as_quad_map, face_as_quad_map);
317
318 Array<Array<float4>> results(uv_maps.size());
319 threading::parallel_for(uv_maps.index_range(), 1, [&](const IndexRange range) {
320 for (const int64_t i : range) {
321 SGLSLMeshToTangent mesh2tangent{};
322 mesh2tangent.positions = vert_positions;
323 mesh2tangent.faces = faces;
324 mesh2tangent.corner_verts = corner_verts;
325 mesh2tangent.corner_tris = corner_tris;
326 mesh2tangent.tri_faces = corner_tri_faces;
327 mesh2tangent.sharp_faces = sharp_faces;
328 mesh2tangent.vert_normals = vert_normals;
329 mesh2tangent.face_normals = face_normals;
330 mesh2tangent.corner_normals = corner_normals;
331 mesh2tangent.uv_map = uv_maps[i];
332
333 mesh2tangent.face_as_quad_map = face_as_quad_map;
334 mesh2tangent.num_face_as_quad_map = num_face_as_quad_map;
335
336 results[i].reinitialize(corner_verts.size());
337 mesh2tangent.tangents = results[i];
338
339 mikk::Mikktspace<SGLSLMeshToTangent> mikk(mesh2tangent);
340 mikk.genTangSpace();
341 }
342 });
343
344 MEM_SAFE_FREE(face_as_quad_map);
345
346 return results;
347}
348
351 const Span<int> corner_verts,
352 const Span<int3> corner_tris,
353 const Span<int> corner_tri_faces,
354 const Span<bool> sharp_faces,
355 const Span<float3> vert_normals,
356 const Span<float3> face_normals,
357 const Span<float3> corner_normals,
358 const Span<float3> vert_orco)
359{
360 if (corner_tris.is_empty()) {
361 return {};
362 }
363
364 int num_face_as_quad_map;
365 int *face_as_quad_map = nullptr;
367 faces, corner_tris, corner_tri_faces, num_face_as_quad_map, face_as_quad_map);
368
369 Array<float4> results(corner_verts.size());
370 SGLSLMeshToTangent mesh2tangent{};
371 mesh2tangent.positions = vert_positions;
372 mesh2tangent.faces = faces;
373 mesh2tangent.corner_verts = corner_verts;
374 mesh2tangent.corner_tris = corner_tris;
375 mesh2tangent.tri_faces = corner_tri_faces;
376 mesh2tangent.vert_normals = vert_normals;
377 mesh2tangent.face_normals = face_normals;
378 mesh2tangent.corner_normals = corner_normals;
379 mesh2tangent.sharp_faces = sharp_faces;
380 mesh2tangent.orco = vert_orco;
381
382 mesh2tangent.face_as_quad_map = face_as_quad_map;
383 mesh2tangent.num_face_as_quad_map = num_face_as_quad_map;
384
385 mesh2tangent.tangents = results;
386
388 mikk.genTangSpace();
389
390 MEM_SAFE_FREE(face_as_quad_map);
391
392 return results;
393}
394
395} // namespace blender::bke::mesh
396
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
float normal_quad_v3(float n[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3])
Definition math_geom.cc:58
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
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)
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
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
static char faces[256]
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)
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)
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)
void calc_uv_tangent_tris_quads(Span< float3 > vert_positions, OffsetIndices< int > faces, Span< int > corner_verts, Span< float3 > corner_normals, Span< float2 > uv_map, MutableSpan< float4 > results, ReportList *reports)
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, 2 > float2
VecBase< int32_t, 3 > int3
mikk::float3 GetTexCoord(const uint face_num, const uint vert_num)
mikk::float3 GetPosition(const uint face_num, const uint vert_num)
uint GetNumVerticesOfFace(const uint face_num)
void SetTangentSpace(const uint face_num, const uint vert_num, mikk::float3 T, bool orientation)
mikk::float3 GetNormal(const uint face_num, const uint vert_num)
uint GetLoop(const uint face_num, const uint vert_num, int3 &tri, int &face_index)
mikk::float3 GetNormal(const uint face_num, const uint vert_num)
uint GetNumVerticesOfFace(const uint face_num)
mikk::float3 GetTexCoord(const uint face_num, const uint vert_num)
mikk::float3 GetPosition(const uint face_num, const uint vert_num)
void SetTangentSpace(const uint face_num, const uint vert_num, mikk::float3 T, bool orientation)
i
Definition text_draw.cc:230