Mesh tangent generation is very slow
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
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.
```rust
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.
```rust
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.

### gram-schmidt

Contributor guide
Assessment
This issue has not been assessed yet.