mirror of
https://github.com/bevyengine/bevy
synced 2024-12-21 02:23:08 +00:00
a6adced9ed
# Objective - Remove `derive_more`'s error derivation and replace it with `thiserror` ## Solution - Added `derive_more`'s `error` feature to `deny.toml` to prevent it sneaking back in. - Reverted to `thiserror` error derivation ## Notes Merge conflicts were too numerous to revert the individual changes, so this reversion was done manually. Please scrutinise carefully during review.
142 lines
4.9 KiB
Rust
142 lines
4.9 KiB
Rust
use super::{Indices, Mesh, VertexAttributeValues};
|
|
use bevy_math::Vec3;
|
|
use thiserror::Error;
|
|
use wgpu_types::{PrimitiveTopology, VertexFormat};
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#[derive(Error, Debug)]
|
|
/// Failed to generate tangents for the mesh.
|
|
pub enum GenerateTangentsError {
|
|
#[error("cannot generate tangents for {0:?}")]
|
|
UnsupportedTopology(PrimitiveTopology),
|
|
#[error("missing indices")]
|
|
MissingIndices,
|
|
#[error("missing vertex attributes '{0}'")]
|
|
MissingVertexAttribute(&'static str),
|
|
#[error("the '{0}' vertex attribute should have {1:?} format")]
|
|
InvalidVertexAttributeFormat(&'static str, VertexFormat),
|
|
#[error("mesh not suitable for tangent generation")]
|
|
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()
|
|
}
|
|
}
|