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
use nalgebra::{Matrix3, Vector3};
use crate::{misc::FloatingPoint, prelude::SurfaceTessellation3D};
/// A tangent space
#[derive(Debug, Clone)]
pub struct TangentSpace<T: FloatingPoint> {
normal: Vector3<T>,
tangent: Vector3<T>,
bitangent: Vector3<T>,
}
impl<T: FloatingPoint> TangentSpace<T> {
/// Create a new tangent space
pub fn new(normal: Vector3<T>, tangent: Vector3<T>, bitangent: Vector3<T>) -> Self {
Self {
normal,
tangent,
bitangent,
}
}
/// Get the normal
pub fn normal(&self) -> &Vector3<T> {
&self.normal
}
/// Get the tangent
pub fn tangent(&self) -> &Vector3<T> {
&self.tangent
}
/// Get the bitangent
pub fn bitangent(&self) -> &Vector3<T> {
&self.bitangent
}
/// Get the TBN matrix
pub fn matrix(&self) -> Matrix3<T> {
Matrix3::new(
self.tangent.x,
self.bitangent.x,
self.normal.x,
self.tangent.y,
self.bitangent.y,
self.normal.y,
self.tangent.z,
self.bitangent.z,
self.normal.z,
)
}
}
impl<T: FloatingPoint> SurfaceTessellation3D<T> {
/// Compute the tangent space for each vertex
///
/// Returns a vector of TangentSpace for each vertex.
pub fn compute_tangent_space(&self) -> Vec<TangentSpace<T>> {
// Initialize accumulators for tangents and bitangents
let mut tangents = vec![Vector3::zeros(); self.points.len()];
let mut bitangents = vec![Vector3::zeros(); self.points.len()];
// Process each triangle face
for face in &self.faces {
let i0 = face[0];
let i1 = face[1];
let i2 = face[2];
// Get vertex positions
let v0 = &self.points[i0];
let v1 = &self.points[i1];
let v2 = &self.points[i2];
// Get UV coordinates
let uv0 = &self.uvs[i0];
let uv1 = &self.uvs[i1];
let uv2 = &self.uvs[i2];
// Calculate edge vectors
let edge1 = v1 - v0;
let edge2 = v2 - v0;
// Calculate UV deltas
let delta_uv1 = uv1 - uv0;
let delta_uv2 = uv2 - uv0;
// Calculate tangent and bitangent using the formula:
// [T B] = 1/det * [deltaV2 -deltaV1] * [edge1]
// [-deltaU2 deltaU1] [edge2]
let det = delta_uv1.x * delta_uv2.y - delta_uv2.x * delta_uv1.y;
if det.abs() > T::default_epsilon() {
let inv_det = T::one() / det;
// Tangent = (deltaV2 * edge1 - deltaV1 * edge2) * inv_det
let tangent = (edge1.scale(delta_uv2.y) - edge2.scale(delta_uv1.y)).scale(inv_det);
// Bitangent = (-deltaU2 * edge1 + deltaU1 * edge2) * inv_det
let bitangent =
(edge2.scale(delta_uv1.x) - edge1.scale(delta_uv2.x)).scale(inv_det);
// Accumulate for each vertex of the triangle
tangents[i0] += tangent;
tangents[i1] += tangent;
tangents[i2] += tangent;
bitangents[i0] += bitangent;
bitangents[i1] += bitangent;
bitangents[i2] += bitangent;
}
}
// Normalize and orthogonalize the tangents
tangents
.into_iter()
.zip(bitangents)
.zip(self.normals.iter())
.map(|((t, b), n)| {
// Gram-Schmidt orthogonalization
// T' = normalize(T - (T·N)N)
let n_dot_t = n.dot(&t);
let tangent = (t - n.scale(n_dot_t)).normalize();
// Calculate handedness and adjust bitangent
// B' = normalize(cross(N, T')) * handedness
let computed_bitangent = n.cross(&tangent);
let handedness = if computed_bitangent.dot(&b) < T::zero() {
-T::one()
} else {
T::one()
};
let bitangent = computed_bitangent.scale(handedness);
TangentSpace {
normal: *n,
tangent,
bitangent,
}
})
.collect()
}
}