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