1use crate::vertex_attributes::MultiIndexedVertexAttributes;
2use crate::BlenderMesh;
3
4#[derive(Debug, Fail)]
6pub enum TangentError {
7 #[fail(display = "Cannot calculate vertex tangents for a mesh with no uvs")]
8 NoVertexUvs,
9}
10
11impl BlenderMesh {
12 pub(crate) fn calculate_face_tangents(&self) -> Result<Vec<f32>, TangentError> {
25 let multi = &self.multi_indexed_vertex_attributes;
26
27 let MultiIndexedVertexAttributes {
28 vertices_in_each_face,
29 positions,
30 uvs,
31 ..
32 } = multi;
33
34 if uvs.is_none() {
35 return Err(TangentError::NoVertexUvs)?;
36 }
37 let uvs = uvs.as_ref().unwrap();
38
39 let mut total_indices_processed = 0;
40
41 let mut face_tangents = vec![];
42
43 for vertices_in_face in vertices_in_each_face.iter() {
45 let vertices_in_face = *vertices_in_face;
46
47 let idx = total_indices_processed as usize;
48
49 let pos_idx_0 = positions.indices[idx];
51 let pos_idx_1 = positions.indices[idx + 1];
52 let pos_idx_2 = positions.indices[idx + 2];
53
54 let uv_idx_0 = uvs.indices[idx];
56 let uv_idx_1 = uvs.indices[idx + 1];
57 let uv_idx_2 = uvs.indices[idx + 2];
58
59 let pos0 = positions.attribute.data_at_idx(pos_idx_0);
60 let pos1 = positions.attribute.data_at_idx(pos_idx_1);
61 let pos2 = positions.attribute.data_at_idx(pos_idx_2);
62
63 let uv0 = uvs.attribute.data_at_idx(uv_idx_0);
64 let uv1 = uvs.attribute.data_at_idx(uv_idx_1);
65 let uv2 = uvs.attribute.data_at_idx(uv_idx_2);
66
67 let edge1 = (pos1[0] - pos0[0], pos1[1] - pos0[1], pos1[2] - pos0[2]);
68 let edge2 = (pos2[0] - pos1[0], pos2[1] - pos1[1], pos2[2] - pos1[2]);
69
70 let delta_uv1 = (uv1[0] - uv0[0], uv1[1] - uv0[1]);
71 let delta_uv2 = (uv2[0] - uv1[0], uv2[1] - uv1[1]);
72
73 let f = 1.0 / ((delta_uv1.0 * delta_uv2.1) - (delta_uv2.0 * delta_uv1.1));
74
75 let tangent_x = f * ((delta_uv2.1 * edge1.0) - (delta_uv1.1 * edge2.0));
76 let tangent_y = f * ((delta_uv2.1 * edge1.1) - (delta_uv1.1 * edge2.1));
77 let tangent_z = f * ((delta_uv2.1 * edge1.2) - (delta_uv1.1 * edge2.2));
78
79 face_tangents.push(tangent_x);
80 face_tangents.push(tangent_y);
81 face_tangents.push(tangent_z);
82
83 total_indices_processed += vertices_in_face as u16;
84 }
85
86 Ok(face_tangents)
87 }
88}
89
90pub(crate) fn face_tangent_at_idx(face_tangents: &[f32], face_idx: usize) -> (f32, f32, f32) {
92 (
93 face_tangents[face_idx * 3],
94 face_tangents[face_idx * 3 + 1],
95 face_tangents[face_idx * 3 + 2],
96 )
97}
98
99#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::concat_vecs;
106 use crate::test_utils::*;
107
108 #[test]
110 fn no_vertex_uvs() {
111 let mesh: BlenderMesh = BlenderMesh::default();
112
113 match mesh.calculate_face_tangents() {
114 Ok(_) => unreachable!(),
115 Err(TangentError::NoVertexUvs) => {}
116 }
117 }
118
119 #[test]
121 fn calculate_tangents_1_triangle() {
122 let mesh: BlenderMesh = BlenderMesh {
123 multi_indexed_vertex_attributes: MultiIndexedVertexAttributes {
124 positions: (
125 vec![0, 1, 2],
126 (
127 concat_vecs!(v(0), vec![1.0, 0.0, 0.0], vec![1.0, 1.0, 0.0]),
128 3,
129 )
130 .into(),
131 )
132 .into(),
133 uvs: Some(
134 (
135 vec![0, 1, 2],
136 (concat_vecs!(v2(0), vec![0.5, 0.0], v2(1)), 2).into(),
137 )
138 .into(),
139 ),
140 vertices_in_each_face: vec![3],
141 ..MultiIndexedVertexAttributes::default()
142 },
143 ..BlenderMesh::default()
144 };
145
146 mesh.calculate_face_tangents().unwrap();
147
148 assert_eq!(
149 &mesh.calculate_face_tangents().unwrap(),
150 &vec![2., 0., 0.]
152 );
153 }
154
155 #[test]
156 fn calculate_tangents_2_triangle() {
157 let mesh: BlenderMesh = BlenderMesh {
158 multi_indexed_vertex_attributes: MultiIndexedVertexAttributes {
159 positions: (
160 vec![0, 1, 2, 0, 2, 3],
161 (
162 concat_vecs!(
163 v(0),
164 vec![1.0, 0.0, 0.0],
165 vec![1.0, 1.0, 0.0],
166 vec![0., 1., 0.]
167 ),
168 3,
169 )
170 .into(),
171 )
172 .into(),
173 uvs: Some(
174 (
175 vec![0, 1, 2, 0, 2, 3],
176 (concat_vecs!(v2(0), vec![0.5, 0.0], v2(1), vec![0., 1.]), 2).into(),
177 )
178 .into(),
179 ),
180 vertices_in_each_face: vec![3, 3],
181 ..MultiIndexedVertexAttributes::default()
182 },
183 ..BlenderMesh::default()
184 };
185
186 mesh.calculate_face_tangents().unwrap();
187
188 assert_eq!(
189 &mesh.calculate_face_tangents().unwrap(),
190 &vec![2., 0., 0., 1., 0., 0.]
192 );
193 }
194
195 #[test]
196 fn calculate_tangents_1_quad() {
197 let mesh: BlenderMesh = BlenderMesh {
198 multi_indexed_vertex_attributes: MultiIndexedVertexAttributes {
199 positions: (
200 vec![0, 1, 2, 3],
201 (
202 concat_vecs!(
203 v(0),
204 vec![1.0, 0.0, 0.0],
205 vec![1.0, 1.0, 0.0],
206 vec![0., 1., 0.]
207 ),
208 3,
209 )
210 .into(),
211 )
212 .into(),
213 uvs: Some(
214 (
215 vec![0, 1, 2, 3],
216 (concat_vecs!(v2(0), vec![0.5, 0.0], v2(1), vec![0., 1.]), 2).into(),
217 )
218 .into(),
219 ),
220 vertices_in_each_face: vec![4],
221 ..MultiIndexedVertexAttributes::default()
222 },
223 ..BlenderMesh::default()
224 };
225
226 mesh.calculate_face_tangents().unwrap();
227
228 assert_eq!(
229 &mesh.calculate_face_tangents().unwrap(),
230 &vec![2., 0., 0.]
232 );
233 }
234}