bevyengine/bevy · error · GenerateTangentsError
mesh not suitable for tangent generation
Error message
mesh not suitable for tangent generation
What it means
GenerateTangentsError::MikktspaceError (crates/bevy_mesh/src/mikktspace.rs:73) wraps bevy_mikktspace::GenerateTangentSpaceError: the underlying MikkTSpace implementation ran over the mesh and gave up, meaning the geometry itself is unsuitable for tangent generation. Typical culprits are degenerate (zero-area) triangles, NaN/inf components in positions or UVs, or UVs collapsed so that no valid tangent frame exists.
Source
Thrown at crates/bevy_mesh/src/mikktspace.rs:73
vert: usize,
) {
let idx = self.index(face, vert);
self.tangents[idx] = tangent_space.unwrap_or_default().tangent_encoded();
}
}
#[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(#[from] bevy_mikktspace::GenerateTangentSpaceError),
#[error("Mesh access error: {0}")]
MeshAccessError(#[from] MeshAccessError),
}
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.try_attribute_option(Mesh::ATTRIBUTE_POSITION)?.ok_or(
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
)?;
let VertexAttributeValues::Float32x3(positions) = positions else {
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(View on GitHub (pinned to 396ca72708)
Solutions
- Sanitize the mesh: reject or drop triangles where two vertex indices coincide or positions are equal (zero area)
- Check positions and UVs for non-finite values and clamp/repair them before compute_tangents()
- Give UVs a non-degenerate mapping (identical UVs on every vertex cannot define a tangent frame)
Example fix
// before: degenerate soup passes format checks but fails inside mikktspace
mesh.compute_tangents(); // Err(MikktspaceError(GenerateTangentSpaceError))
// after: drop degenerate triangles first
let Indices::U32(idx) = mesh.indices().unwrap() else { unreachable!() };
let kept: Vec<[u32; 3]> = idx.as_chunks().0.iter().copied()
.filter(|&[a, b, c]| a != b && b != c && a != c)
.collect();
mesh.insert_indices(Indices::U32(kept.into_iter().flatten().collect()));
mesh.compute_tangents().unwrap(); Defensive patterns
Strategy: validation
Validate before calling
fn geometry_sane_for_tangents(mesh: &Mesh) -> bool {
let ok_f32 = |x: &f32| x.is_finite();
let positions_ok = mesh
.attribute(Mesh::ATTRIBUTE_POSITION)
.and_then(|v| v.as_float3())
.is_some_and(|p| p.iter().flatten().all(ok_f32));
let uvs_ok = mesh
.attribute(Mesh::ATTRIBUTE_UV_0)
.and_then(|v| v.as_float2())
.is_some_and(|u| u.iter().flatten().all(ok_f32));
let no_degenerate = mesh.indices().is_some_and(|idx| match idx {
Indices::U16(v) => v.as_chunks().0.iter().all(|&[a, b, c]| a != b && b != c && a != c),
Indices::U32(v) => v.as_chunks().0.iter().all(|&[a, b, c]| a != b && b != c && a != c),
});
positions_ok && uvs_ok && no_degenerate
} Try / catch
match mesh.compute_tangents() {
Ok(()) => {}
Err(GenerateTangentsError::MikktspaceError(e)) => {
bevy::log::error!("geometry unsuitable for tangents: {e}; check degenerate triangles/UVs");
// fall back: skip tangents or sanitize mesh and retry once
}
Err(e) => bevy::log::error!("{e}"),
} Prevention
- Filter zero-area triangles (repeated indices, coincident positions) before tangent generation
- Reject NaN/inf in positions and UVs at import time
- Ensure UVs are not globally collapsed to a single value
When it happens
Trigger: Calling mesh.compute_tangents() after topology, indices, and attribute presence/format checks have all passed, but the data contains degenerate triangles (repeated vertex indices, coincident positions), NaNs from bad math, or fully-collapsed UVs (all UVs identical).
Common situations: Procedural meshes with accidental duplicate vertices producing zero-area slivers; heightfield geometry with poles or seams where many triangles collapse to a point; imported assets with uninitialized UV channels set to (0,0) everywhere; division by zero upstream producing NaN positions.
Related errors
- cannot generate tangents for {0:?}
- missing indices
- missing vertex attributes '{0}'
- the '{0}' vertex attribute should have {1:?} format
- failed to generate tangents: {0}
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/b93b2577ffe7220d.
Report an issue: GitHub.