1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
//! Provides a byte-identical implementation of [`mikktspace`] in entirely safe
//! and idiomatic Rust.
//! This is used to generate [`TangentSpace`] values for 3D geometry.
//! Like the original implementation, this crate has no dependencies.
//!
//! With _only_ the default features enabled, this should produce _identical_
//! results to [`mikktspace`] on x86 architectures.
//! Other architectures may produce differing results due to the original
//! implementation's reliance on undefined behavior.
//!
//! # Usage
//!
//! As a preliminary step for `no_std` users, you must provide an implementation
//! for [`Ops`].
//! When the `std` feature is enabled, one is provided and automatically selected
//! as the default.
//!
//! First, implement [`Geometry`] for your geometry.
//!
//! ```ignore
//! impl Geometry for MyGeometry { /* ... */ }
//! ```
//!
//! The interface is how this crate reads geometric information _and_ writes back
//! the generated tangent space information.
//!
//! Finally, use [`generate_tangents`] to calculate and write back all
//! [`TangentSpace`] values.
//!
//! # Description
//!
//! The code is designed to consistently generate the same tangent spaces, for a
//! given mesh, in any tool in which it is used.
//! This is done by performing an internal welding step and subsequently an
//! order-independent evaluation of tangent space for meshes consisting of
//! triangles and quads.
//! This means faces can be received in any order and the same is true for the
//! order of vertices of each face.
//! The generated result will not be affected by such reordering.
//! Additionally, whether degenerate (vertices or texture coordinates) primitives
//! are present or not will not affect the generated results either.
//!
//! Once tangent space calculation is done the vertices of degenerate primitives
//! will simply inherit tangent space from neighboring non degenerate primitives.
//! The analysis behind this implementation can be found in Morten S. Mikkelsen's
//! master's [thesis].
//!
//! Note that though the tangent spaces at the vertices are generated in an
//! order-independent way, by this implementation, the interpolated tangent space
//! is still affected by which diagonal is chosen to split each quad.
//! A sensible solution is to have your tools pipeline always split quads by the
//! shortest diagonal.
//! This choice is order-independent and works with mirroring.
//! If these have the same length then compare the diagonals defined by the
//! texture coordinates.
//! [XNormal], which is a tool for baking normal maps, allows you to write your
//! own tangent space plugin and also quad triangulator plugin.
//!
//! # Features
//!
//! ## `std` (default)
//!
//! Provides access to the standard library, allowing a default implementation
//! of [`Ops`] to be provided.
//! If you disable this feature, you will need to provide a type implementing
//! [`Ops`] as the `O` parameter in the [`Geometry`] trait.
//!
//! ```
//! # use bevy_mikktspace::{Geometry, Ops};
//! # struct MyOps;
//! # struct MyGeometry;
//! impl Ops for MyOps {
//! fn sqrt(x: f32) -> f32 {
//! unimplemented!()
//! }
//!
//! fn acos(x: f32) -> f32 {
//! unimplemented!()
//! }
//! }
//!
//! # #[cfg(any())]
//! impl Geometry<MyOps> for MyGeometry { /* ... */ }
//! ```
//!
//! A common backend for implementing [`Ops`] is [`libm`]:
//!
//! ```
//! # use bevy_mikktspace::Ops;
//! # struct LibmOps;
//! impl Ops for LibmOps {
//! fn sqrt(x: f32) -> f32 {
//! libm::sqrtf(x)
//! }
//!
//! fn acos(x: f32) -> f32 {
//! libm::acos(x as f64) as f32
//! }
//! }
//! ```
//!
//! Note that alternate backends _may_ break byte-compatibility with the original
//! C implementation.
//! It also should go without saying that improper implementations could give
//! entirely incorrect results.
//!
//! ## `corrected-edge-sorting`
//!
//! Fixes a known bug in the original C implementation which can affect the
//! generated values.
//! The bug can cause edges to be improperly sorted, leading neighboring faces
//! to be ungrouped.
//! If you do not need byte compatibility with the C implementation, it is
//! recommended to enable this feature for improved performance, compile times,
//! and correctness.
//!
//! ## `corrected-vertex-welding`
//!
//! Fixes a known bug in the original C implementation which can affect the
//! generated values.
//! This bug causes vertices to be combined in such a way that the lowest vertex
//! index isn't reliably selected.
//! If you do not need byte compatibility with the C implementation, it is
//! recommended to enable this feature for improved performance, compile times,
//! and correctness.
//!
//! # Copyright
//!
//! This code is a Rust reimplementation of <https://github.com/mmikk/MikkTSpace>.
//! The copyright notice below reflects that history, and should not be removed.
//!
//! > Copyright (C) 2011 by Morten S. Mikkelsen
//! >
//! > This software is provided 'as-is', without any express or implied
//! > warranty. In no event will the authors be held liable for any damages
//! > arising from the use of this software.
//! >
//! > Permission is granted to anyone to use this software for any purpose,
//! > including commercial applications, and to alter it and redistribute it
//! > freely, subject to the following restrictions:
//! >
//! > 1. The origin of this software must not be misrepresented; you must not
//! > claim that you wrote the original software. If you use this software
//! > in a product, an acknowledgment in the product documentation would be
//! > appreciated but is not required.
//! > 2. Altered source versions must be plainly marked as such, and must not be
//! > misrepresented as being the original software.
//! > 3. This notice may not be removed or altered from any source distribution.
//!
//! The above notice will also be included in any derivative source files.
//!
//! # Advice
//!
//! To avoid visual errors (distortions/unwanted hard edges in lighting), when
//! using sampled normal maps, the normal map sampler must use the exact inverse
//! of the pixel shader transformation.
//! The most efficient transformation we can possibly do in the pixel shader is
//! achieved by using, directly, the "unnormalized" interpolated tangent, bitangent
//! and vertex normal: `vT`, `vB` and `vN`.
//!
//! ```c, ignore
//! // pixel shader (fast transform out)
//! vNout = normalize(vNt.x * vT + vNt.y * vB + vNt.z * vN);
//! ```
//!
//! where `vNt` is the tangent space normal.
//!
//! The normal map sampler must likewise use the interpolated and "unnormalized"
//! tangent, bitangent and vertex normal to be compliant with the pixel shader.
//!
//! ```c, ignore
//! // sampler does (exact inverse of pixel shader):
//! float3 row0 = cross(vB, vN);
//! float3 row1 = cross(vN, vT);
//! float3 row2 = cross(vT, vB);
//! float fSign = dot(vT, row0)<0 ? -1 : 1;
//! vNt = normalize(fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)));
//! ```
//!
//! where `vNout` is the sampled normal in some chosen 3D space.
//!
//! Should you choose to reconstruct the bitangent in the pixel shader instead of
//! the vertex shader, as explained earlier, then be sure to do this in the normal
//! map sampler also.
//!
//! Finally, beware of quad triangulations.
//! If the normal map sampler doesn't use the same triangulation of quads as your
//! renderer then problems will occur since the interpolated tangent spaces will
//! differ even though the vertex level tangent spaces match.
//! This can be solved either by triangulating before sampling/exporting or by
//! using the order-independent choice of diagonal for splitting quads suggested earlier.
//! However, this must be used both by the sampler and your tools/rendering pipeline.
//!
//! [`mikktspace`]: http://www.mikktspace.com/
//! [thesis]: https://web.archive.org/web/20250321012901/https://image.diku.dk/projects/media/morten.mikkelsen.08.pdf
//! [XNormal]: https://xnormal.net/
//! [`libm`]: https://docs.rs/libm
extern crate alloc;
pub use Ops;
pub use StdOps;
/// Generates [`TangentSpace`]s for the provided geometry with the grouping
/// threshold disabled.
/// Generates [`TangentSpace`]s for the provided geometry with a provided `threshold`
/// for vertex grouping.
///
/// Note that unlike the original C implementation, which accepted an _angular_ threshold,
/// this function accepts a _linear_ threshold.
/// The angular threshold can be converted into a linear one trivially using cosine.
///
/// ```ignore
/// let angular_threshold = 180_f32;
/// let linear_threshold = angular_threshold.to_radians().cos();
/// ```
///
/// Appropriate threshold values should be in the range `[-1..=1]`.
/// Provides an interface for reading vertex information from geometry, and writing
/// back out the calculated tangent space information.
///
/// Without the `std` feature, there is no default implementation for [`Ops`]
/// provided.
/// Instead, you must also provide a type implementing [`Ops`] using an alternative
/// math backend, such as [`libm`].
///
/// [`libm`]: https://docs.rs/libm
/// Wraps the relevant results generated when calculating the tangent space for
/// a particular vertex on a particular face.
///
/// Typically, you will call [`tangent`](TangentSpace::tangent) to retrieve the
/// tangent value.
/// Error returned when failing to generate tangent spaces for a geometry.
// Reserving the right to introduce new error variants in the future.