Description
What problem does this solve or what need does it fill?
As the title states, generating tangents is quite slow. It's less of an issue when it's a one time operation but becomes more noticeable for frequent mesh updates or when generating many meshes.
What solution would you like?
It would be nice if the API allowed for the user to select which algorithm is used for tangent computation.
impl Mesh {
#[deprecated(note = "use compute_tangents")]
pub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError> { .. }
pub fn compute_tangents(&mut self, tangent_strategy: TangentStrategy) -> Result<(), GenerateTangentsError> { .. }
pub fn compute_normals(&mut self) { .. }
}
pub enum TangentStrategy {
/// Uses the Gram-Schmidt fast approximation algorithm. Produces potentially lower quality tangents, but very fast.
FastApproximation,
/// Uses the `mikktspace` algorithm. Produces potentially higher quality tangents, but much slower.
HighQuality,
}
I'm current using the following to compute the Gram-Schmidt tangents:
EDIT: This example is incorrect and producing a tangent per face instead of per vertex.
fn compute_tangents_fast(mesh: &mut Mesh) -> Vec<[f32; 4]> {
let trinagle_count = mesh.indices().expect("indices").len() / 3;
let mut tangents = Vec::with_capacity(trinagle_count);
for triangle in mesh.triangles().expect("triangles") {
let normal = Vec3::from(triangle.normal().expect("normal")).into();
let verts = triangle.vertices;
let verts: [Vec3A; 3] = [verts[0].into(), verts[1].into(), verts[2].into()];
let edge1 = verts[1] - verts[0];
let mut tangent = edge1 - normal * edge1.dot(normal);
if tangent.length_squared() < f32::EPSILON {
let edge2 = verts[2] - verts[1];
tangent = edge2 - normal * edge2.dot(normal)
}
tangent = tangent.normalize();
let bitangent_approximation = normal.cross(tangent).normalize();
let w = if normal.cross(tangent).dot(bitangent_approximation) < 0.0 {
-1.0
} else {
1.0
};
tangents.push(tangent.extend(w).to_array());
}
tangents
}
The method above is more than 20x
faster for the terrain mesh chunks that I'm working on and I can't visually tell any difference between them.
generate_tangents: 2.4916 ms (mikktspace)
compute_tangents_fast: 0.1065 ms (Gram-Schmidt)
What alternative(s) have you considered?
I have a working alternative but it would be nice if this was in the bevy API.
Additional context
I can clean up the example code and start a PR if there's interest in this.
mikktspace
note: the thin green line is an axis poking up through the mesh. I think the camera was oriented very slightly differently.