Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_mesh/src/mikktspace.rs
6595 views
1
use super::{Indices, Mesh, VertexAttributeValues};
2
use bevy_math::Vec3;
3
use thiserror::Error;
4
use wgpu_types::{PrimitiveTopology, VertexFormat};
5
6
struct 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
14
impl 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
26
impl 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)]
62
/// Failed to generate tangents for the mesh.
63
pub 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
76
pub(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
// mikktspace seems to assume left-handedness so we can flip the sign to correct for this
124
for tangent in &mut mikktspace_mesh.tangents {
125
tangent[3] = -tangent[3];
126
}
127
128
Ok(mikktspace_mesh.tangents)
129
}
130
131
/// Correctly scales and renormalizes an already normalized `normal` by the scale determined by its reciprocal `scale_recip`
132
pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
133
// This is basically just `normal * scale_recip` but with the added rule that `0. * anything == 0.`
134
// This is necessary because components of `scale_recip` may be infinities, which do not multiply to zero
135
let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
136
137
// If n is finite, no component of `scale_recip` was infinite or the normal was perpendicular to the scale
138
// else the scale had at least one zero-component and the normal needs to point along the direction of that component
139
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
}
145
146