1use super::{Indices, Mesh, VertexAttributeValues};
2use bevy_math::Vec3;
3use derive_more::derive::{Display, Error};
4use wgpu::{PrimitiveTopology, VertexFormat};
5
6struct MikktspaceGeometryHelper<'a> {
7 indices: Option<&'a Indices>,
8 positions: &'a Vec<[f32; 3]>,
9 normals: &'a Vec<[f32; 3]>,
10 uvs: &'a Vec<[f32; 2]>,
11 tangents: Vec<[f32; 4]>,
12}
13
14impl MikktspaceGeometryHelper<'_> {
15 fn index(&self, face: usize, vert: usize) -> usize {
16 let index_index = face * 3 + vert;
17
18 match self.indices {
19 Some(Indices::U16(indices)) => indices[index_index] as usize,
20 Some(Indices::U32(indices)) => indices[index_index] as usize,
21 None => index_index,
22 }
23 }
24}
25
26impl bevy_mikktspace::Geometry for MikktspaceGeometryHelper<'_> {
27 fn num_faces(&self) -> usize {
28 self.indices
29 .map(Indices::len)
30 .unwrap_or_else(|| self.positions.len())
31 / 3
32 }
33
34 fn num_vertices_of_face(&self, _: usize) -> usize {
35 3
36 }
37
38 fn position(&self, face: usize, vert: usize) -> [f32; 3] {
39 self.positions[self.index(face, vert)]
40 }
41
42 fn normal(&self, face: usize, vert: usize) -> [f32; 3] {
43 self.normals[self.index(face, vert)]
44 }
45
46 fn tex_coord(&self, face: usize, vert: usize) -> [f32; 2] {
47 self.uvs[self.index(face, vert)]
48 }
49
50 fn set_tangent_encoded(&mut self, tangent: [f32; 4], face: usize, vert: usize) {
51 let idx = self.index(face, vert);
52 self.tangents[idx] = tangent;
53 }
54}
55
56#[derive(Error, Display, Debug)]
57pub enum GenerateTangentsError {
59 #[display("cannot generate tangents for {_0:?}")]
60 #[error(ignore)]
61 UnsupportedTopology(PrimitiveTopology),
62 #[display("missing indices")]
63 MissingIndices,
64 #[display("missing vertex attributes '{_0}'")]
65 #[error(ignore)]
66 MissingVertexAttribute(&'static str),
67 #[display("the '{_0}' vertex attribute should have {_1:?} format")]
68 #[error(ignore)]
69 InvalidVertexAttributeFormat(&'static str, VertexFormat),
70 #[display("mesh not suitable for tangent generation")]
71 MikktspaceError,
72}
73
74pub(crate) fn generate_tangents_for_mesh(
75 mesh: &Mesh,
76) -> Result<Vec<[f32; 4]>, GenerateTangentsError> {
77 match mesh.primitive_topology() {
78 PrimitiveTopology::TriangleList => {}
79 other => return Err(GenerateTangentsError::UnsupportedTopology(other)),
80 };
81
82 let positions = mesh.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
83 GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
84 )?;
85 let VertexAttributeValues::Float32x3(positions) = positions else {
86 return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
87 Mesh::ATTRIBUTE_POSITION.name,
88 VertexFormat::Float32x3,
89 ));
90 };
91 let normals = mesh.attribute(Mesh::ATTRIBUTE_NORMAL).ok_or(
92 GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_NORMAL.name),
93 )?;
94 let VertexAttributeValues::Float32x3(normals) = normals else {
95 return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
96 Mesh::ATTRIBUTE_NORMAL.name,
97 VertexFormat::Float32x3,
98 ));
99 };
100 let uvs = mesh.attribute(Mesh::ATTRIBUTE_UV_0).ok_or(
101 GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_UV_0.name),
102 )?;
103 let VertexAttributeValues::Float32x2(uvs) = uvs else {
104 return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
105 Mesh::ATTRIBUTE_UV_0.name,
106 VertexFormat::Float32x2,
107 ));
108 };
109
110 let len = positions.len();
111 let tangents = vec![[0., 0., 0., 0.]; len];
112 let mut mikktspace_mesh = MikktspaceGeometryHelper {
113 indices: mesh.indices(),
114 positions,
115 normals,
116 uvs,
117 tangents,
118 };
119 let success = bevy_mikktspace::generate_tangents(&mut mikktspace_mesh);
120 if !success {
121 return Err(GenerateTangentsError::MikktspaceError);
122 }
123
124 for tangent in &mut mikktspace_mesh.tangents {
126 tangent[3] = -tangent[3];
127 }
128
129 Ok(mikktspace_mesh.tangents)
130}
131
132pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
134 let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
137
138 if n.is_finite() {
141 n.normalize_or_zero()
142 } else {
143 Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()
144 }
145}