Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_mesh/src/mikktspace.rs
9401 views
1
use crate::MeshAccessError;
2
3
use super::{Indices, Mesh, VertexAttributeValues};
4
use thiserror::Error;
5
use wgpu_types::{PrimitiveTopology, VertexFormat};
6
7
struct 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
15
impl 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
27
impl 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)]
63
/// Failed to generate tangents for the mesh.
64
pub 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
79
pub(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
// mikktspace seems to assume left-handedness so we can flip the sign to correct for this
127
for tangent in &mut mikktspace_mesh.tangents {
128
tangent[3] = -tangent[3];
129
}
130
131
Ok(mikktspace_mesh.tangents)
132
}
133
134