2024-10-06 14:18:11 +00:00
|
|
|
use super::{Indices, Mesh, VertexAttributeValues};
|
|
|
|
use bevy_math::Vec3;
|
2024-12-06 17:03:55 +00:00
|
|
|
use thiserror::Error;
|
2024-12-03 19:49:49 +00:00
|
|
|
use wgpu_types::{PrimitiveTopology, VertexFormat};
|
2024-10-06 14:18:11 +00:00
|
|
|
|
|
|
|
struct MikktspaceGeometryHelper<'a> {
|
|
|
|
indices: Option<&'a Indices>,
|
|
|
|
positions: &'a Vec<[f32; 3]>,
|
|
|
|
normals: &'a Vec<[f32; 3]>,
|
|
|
|
uvs: &'a Vec<[f32; 2]>,
|
|
|
|
tangents: Vec<[f32; 4]>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MikktspaceGeometryHelper<'_> {
|
|
|
|
fn index(&self, face: usize, vert: usize) -> usize {
|
|
|
|
let index_index = face * 3 + vert;
|
|
|
|
|
|
|
|
match self.indices {
|
|
|
|
Some(Indices::U16(indices)) => indices[index_index] as usize,
|
|
|
|
Some(Indices::U32(indices)) => indices[index_index] as usize,
|
|
|
|
None => index_index,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl bevy_mikktspace::Geometry for MikktspaceGeometryHelper<'_> {
|
|
|
|
fn num_faces(&self) -> usize {
|
|
|
|
self.indices
|
|
|
|
.map(Indices::len)
|
|
|
|
.unwrap_or_else(|| self.positions.len())
|
|
|
|
/ 3
|
|
|
|
}
|
|
|
|
|
|
|
|
fn num_vertices_of_face(&self, _: usize) -> usize {
|
|
|
|
3
|
|
|
|
}
|
|
|
|
|
|
|
|
fn position(&self, face: usize, vert: usize) -> [f32; 3] {
|
|
|
|
self.positions[self.index(face, vert)]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn normal(&self, face: usize, vert: usize) -> [f32; 3] {
|
|
|
|
self.normals[self.index(face, vert)]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tex_coord(&self, face: usize, vert: usize) -> [f32; 2] {
|
|
|
|
self.uvs[self.index(face, vert)]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_tangent_encoded(&mut self, tangent: [f32; 4], face: usize, vert: usize) {
|
|
|
|
let idx = self.index(face, vert);
|
|
|
|
self.tangents[idx] = tangent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 17:03:55 +00:00
|
|
|
#[derive(Error, Debug)]
|
2024-10-06 14:18:11 +00:00
|
|
|
/// Failed to generate tangents for the mesh.
|
|
|
|
pub enum GenerateTangentsError {
|
2024-12-06 17:03:55 +00:00
|
|
|
#[error("cannot generate tangents for {0:?}")]
|
2024-10-06 14:18:11 +00:00
|
|
|
UnsupportedTopology(PrimitiveTopology),
|
2024-12-06 17:03:55 +00:00
|
|
|
#[error("missing indices")]
|
2024-10-06 14:18:11 +00:00
|
|
|
MissingIndices,
|
2024-12-06 17:03:55 +00:00
|
|
|
#[error("missing vertex attributes '{0}'")]
|
2024-10-06 14:18:11 +00:00
|
|
|
MissingVertexAttribute(&'static str),
|
2024-12-06 17:03:55 +00:00
|
|
|
#[error("the '{0}' vertex attribute should have {1:?} format")]
|
2024-10-06 14:18:11 +00:00
|
|
|
InvalidVertexAttributeFormat(&'static str, VertexFormat),
|
2024-12-06 17:03:55 +00:00
|
|
|
#[error("mesh not suitable for tangent generation")]
|
2024-10-06 14:18:11 +00:00
|
|
|
MikktspaceError,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn generate_tangents_for_mesh(
|
|
|
|
mesh: &Mesh,
|
|
|
|
) -> Result<Vec<[f32; 4]>, GenerateTangentsError> {
|
|
|
|
match mesh.primitive_topology() {
|
|
|
|
PrimitiveTopology::TriangleList => {}
|
|
|
|
other => return Err(GenerateTangentsError::UnsupportedTopology(other)),
|
|
|
|
};
|
|
|
|
|
|
|
|
let positions = mesh.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
|
|
|
|
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
|
|
|
|
)?;
|
|
|
|
let VertexAttributeValues::Float32x3(positions) = positions else {
|
|
|
|
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
|
|
|
|
Mesh::ATTRIBUTE_POSITION.name,
|
|
|
|
VertexFormat::Float32x3,
|
|
|
|
));
|
|
|
|
};
|
|
|
|
let normals = mesh.attribute(Mesh::ATTRIBUTE_NORMAL).ok_or(
|
|
|
|
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_NORMAL.name),
|
|
|
|
)?;
|
|
|
|
let VertexAttributeValues::Float32x3(normals) = normals else {
|
|
|
|
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
|
|
|
|
Mesh::ATTRIBUTE_NORMAL.name,
|
|
|
|
VertexFormat::Float32x3,
|
|
|
|
));
|
|
|
|
};
|
|
|
|
let uvs = mesh.attribute(Mesh::ATTRIBUTE_UV_0).ok_or(
|
|
|
|
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_UV_0.name),
|
|
|
|
)?;
|
|
|
|
let VertexAttributeValues::Float32x2(uvs) = uvs else {
|
|
|
|
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
|
|
|
|
Mesh::ATTRIBUTE_UV_0.name,
|
|
|
|
VertexFormat::Float32x2,
|
|
|
|
));
|
|
|
|
};
|
|
|
|
|
|
|
|
let len = positions.len();
|
|
|
|
let tangents = vec![[0., 0., 0., 0.]; len];
|
|
|
|
let mut mikktspace_mesh = MikktspaceGeometryHelper {
|
|
|
|
indices: mesh.indices(),
|
|
|
|
positions,
|
|
|
|
normals,
|
|
|
|
uvs,
|
|
|
|
tangents,
|
|
|
|
};
|
|
|
|
let success = bevy_mikktspace::generate_tangents(&mut mikktspace_mesh);
|
|
|
|
if !success {
|
|
|
|
return Err(GenerateTangentsError::MikktspaceError);
|
|
|
|
}
|
|
|
|
|
|
|
|
// mikktspace seems to assume left-handedness so we can flip the sign to correct for this
|
|
|
|
for tangent in &mut mikktspace_mesh.tangents {
|
|
|
|
tangent[3] = -tangent[3];
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(mikktspace_mesh.tangents)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Correctly scales and renormalizes an already normalized `normal` by the scale determined by its reciprocal `scale_recip`
|
|
|
|
pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
|
|
|
|
// This is basically just `normal * scale_recip` but with the added rule that `0. * anything == 0.`
|
|
|
|
// This is necessary because components of `scale_recip` may be infinities, which do not multiply to zero
|
|
|
|
let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
|
|
|
|
|
|
|
|
// If n is finite, no component of `scale_recip` was infinite or the normal was perpendicular to the scale
|
|
|
|
// else the scale had at least one zero-component and the normal needs to point along the direction of that component
|
|
|
|
if n.is_finite() {
|
|
|
|
n.normalize_or_zero()
|
|
|
|
} else {
|
|
|
|
Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()
|
|
|
|
}
|
|
|
|
}
|