1use super::{Indices, Mesh, VertexAttributeValues};
2use bevy_math::Vec3;
3use thiserror::Error;
4use wgpu_types::{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(
51 &mut self,
52 tangent_space: Option<bevy_mikktspace::TangentSpace>,
53 face: usize,
54 vert: usize,
55 ) {
56 let idx = self.index(face, vert);
57 self.tangents[idx] = tangent_space.unwrap_or_default().tangent_encoded();
58 }
59}
60
61#[derive(Error, Debug)]
62pub enum GenerateTangentsError {
64 #[error("cannot generate tangents for {0:?}")]
65 UnsupportedTopology(PrimitiveTopology),
66 #[error("missing indices")]
67 MissingIndices,
68 #[error("missing vertex attributes '{0}'")]
69 MissingVertexAttribute(&'static str),
70 #[error("the '{0}' vertex attribute should have {1:?} format")]
71 InvalidVertexAttributeFormat(&'static str, VertexFormat),
72 #[error("mesh not suitable for tangent generation")]
73 MikktspaceError(#[from] bevy_mikktspace::GenerateTangentSpaceError),
74}
75
76pub(crate) fn generate_tangents_for_mesh(
77 mesh: &Mesh,
78) -> Result<Vec<[f32; 4]>, GenerateTangentsError> {
79 match mesh.primitive_topology() {
80 PrimitiveTopology::TriangleList => {}
81 other => return Err(GenerateTangentsError::UnsupportedTopology(other)),
82 };
83
84 let positions = mesh.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
85 GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
86 )?;
87 let VertexAttributeValues::Float32x3(positions) = positions else {
88 return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
89 Mesh::ATTRIBUTE_POSITION.name,
90 VertexFormat::Float32x3,
91 ));
92 };
93 let normals = mesh.attribute(Mesh::ATTRIBUTE_NORMAL).ok_or(
94 GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_NORMAL.name),
95 )?;
96 let VertexAttributeValues::Float32x3(normals) = normals else {
97 return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
98 Mesh::ATTRIBUTE_NORMAL.name,
99 VertexFormat::Float32x3,
100 ));
101 };
102 let uvs = mesh.attribute(Mesh::ATTRIBUTE_UV_0).ok_or(
103 GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_UV_0.name),
104 )?;
105 let VertexAttributeValues::Float32x2(uvs) = uvs else {
106 return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
107 Mesh::ATTRIBUTE_UV_0.name,
108 VertexFormat::Float32x2,
109 ));
110 };
111
112 let len = positions.len();
113 let tangents = vec![[0., 0., 0., 0.]; len];
114 let mut mikktspace_mesh = MikktspaceGeometryHelper {
115 indices: mesh.indices(),
116 positions,
117 normals,
118 uvs,
119 tangents,
120 };
121 bevy_mikktspace::generate_tangents(&mut mikktspace_mesh)?;
122
123 for tangent in &mut mikktspace_mesh.tangents {
125 tangent[3] = -tangent[3];
126 }
127
128 Ok(mikktspace_mesh.tangents)
129}
130
131pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
133 let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
136
137 if n.is_finite() {
140 n.normalize_or_zero()
141 } else {
142 Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()
143 }
144}