Skip to main content

engvis_core/
math.rs

1pub use glam::{Vec2, Vec3, Vec4, Mat3, Mat4, Quat, Affine3A};
2
3/// Compute tangent vectors from positions, normals, UVs and indices.
4/// Returns a Vec of [f32; 4] where xyz = tangent direction, w = handedness.
5pub fn compute_tangents(
6    positions: &[[f32; 3]],
7    normals: &[[f32; 3]],
8    uvs: &[[f32; 2]],
9    indices: &[u32],
10) -> Vec<[f32; 4]> {
11    let vertex_count = positions.len();
12    let mut tangents = vec![Vec3::ZERO; vertex_count];
13    let mut bitangents = vec![Vec3::ZERO; vertex_count];
14
15    for tri in indices.chunks(3) {
16        if tri.len() < 3 {
17            continue;
18        }
19        let (i0, i1, i2) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
20
21        let p0 = Vec3::from(positions[i0]);
22        let p1 = Vec3::from(positions[i1]);
23        let p2 = Vec3::from(positions[i2]);
24
25        let uv0 = Vec2::from(uvs[i0]);
26        let uv1 = Vec2::from(uvs[i1]);
27        let uv2 = Vec2::from(uvs[i2]);
28
29        let e1 = p1 - p0;
30        let e2 = p2 - p0;
31        let duv1 = uv1 - uv0;
32        let duv2 = uv2 - uv0;
33
34        let f = 1.0 / (duv1.x * duv2.y - duv2.x * duv1.y + 1e-12);
35        let tangent = Vec3::new(
36            f * (duv2.y * e1.x - duv1.y * e2.x),
37            f * (duv2.y * e1.y - duv1.y * e2.y),
38            f * (duv2.y * e1.z - duv1.y * e2.z),
39        );
40        let bitangent = Vec3::new(
41            f * (-duv2.x * e1.x + duv1.x * e2.x),
42            f * (-duv2.x * e1.y + duv1.x * e2.y),
43            f * (-duv2.x * e1.z + duv1.x * e2.z),
44        );
45
46        for &i in &[i0, i1, i2] {
47            tangents[i] += tangent;
48            bitangents[i] += bitangent;
49        }
50    }
51
52    (0..vertex_count)
53        .map(|i| {
54            let n = Vec3::from(normals[i]);
55            let t = tangents[i];
56            let b = bitangents[i];
57
58            // Gram-Schmidt orthogonalize
59            let t_ortho = (t - n * n.dot(t)).normalize_or_zero();
60            let handedness = if n.dot(t_ortho.cross(b)) < 0.0 {
61                -1.0
62            } else {
63                1.0
64            };
65            [t_ortho.x, t_ortho.y, t_ortho.z, handedness]
66        })
67        .collect()
68}