mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
Support calculating normals for indexed meshes (#11654)
# Objective - Finish #3987 ## Solution - Rebase and fix typo. Co-authored-by: Robert Bragg <robert@sixbynine.org>
This commit is contained in:
parent
4f20faaa43
commit
e9343b052f
2 changed files with 54 additions and 23 deletions
|
@ -476,18 +476,10 @@ async fn load_gltf<'a, 'b, 'c>(
|
||||||
if mesh.attribute(Mesh::ATTRIBUTE_NORMAL).is_none()
|
if mesh.attribute(Mesh::ATTRIBUTE_NORMAL).is_none()
|
||||||
&& matches!(mesh.primitive_topology(), PrimitiveTopology::TriangleList)
|
&& matches!(mesh.primitive_topology(), PrimitiveTopology::TriangleList)
|
||||||
{
|
{
|
||||||
let vertex_count_before = mesh.count_vertices();
|
bevy_utils::tracing::debug!(
|
||||||
mesh.duplicate_vertices();
|
"Automatically calculating missing vertex normals for geometry."
|
||||||
|
);
|
||||||
mesh.compute_flat_normals();
|
mesh.compute_flat_normals();
|
||||||
let vertex_count_after = mesh.count_vertices();
|
|
||||||
|
|
||||||
if vertex_count_before != vertex_count_after {
|
|
||||||
bevy_utils::tracing::debug!("Missing vertex normals in indexed geometry, computing them as flat. Vertex count increased from {} to {}", vertex_count_before, vertex_count_after);
|
|
||||||
} else {
|
|
||||||
bevy_utils::tracing::debug!(
|
|
||||||
"Missing vertex normals in indexed geometry, computing them as flat."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(vertex_attribute) = reader
|
if let Some(vertex_attribute) = reader
|
||||||
|
|
|
@ -544,12 +544,13 @@ impl Mesh {
|
||||||
/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
|
/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3` or
|
/// Panics if [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`.
|
||||||
/// if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
|
/// Panics if the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
|
||||||
/// Consider calling [`Mesh::duplicate_vertices`] or export your mesh with normal attributes.
|
///
|
||||||
|
/// FIXME: The should handle more cases since this is called as a part of gltf
|
||||||
|
/// mesh loading where we can't really blame users for loading meshes that might
|
||||||
|
/// not conform to the limitations here!
|
||||||
pub fn compute_flat_normals(&mut self) {
|
pub fn compute_flat_normals(&mut self) {
|
||||||
assert!(self.indices().is_none(), "`compute_flat_normals` can't work on indexed geometry. Consider calling `Mesh::duplicate_vertices`.");
|
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
|
matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
|
||||||
"`compute_flat_normals` can only work on `TriangleList`s"
|
"`compute_flat_normals` can only work on `TriangleList`s"
|
||||||
|
@ -561,18 +562,56 @@ impl Mesh {
|
||||||
.as_float3()
|
.as_float3()
|
||||||
.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
|
.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
|
||||||
|
|
||||||
let normals: Vec<_> = positions
|
match self.indices() {
|
||||||
.chunks_exact(3)
|
Some(indices) => {
|
||||||
.map(|p| face_normal(p[0], p[1], p[2]))
|
let mut count: usize = 0;
|
||||||
.flat_map(|normal| [normal; 3])
|
let mut corners = [0_usize; 3];
|
||||||
.collect();
|
let mut normals = vec![[0.0f32; 3]; positions.len()];
|
||||||
|
let mut adjacency_counts = vec![0_usize; positions.len()];
|
||||||
|
|
||||||
self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
|
for i in indices.iter() {
|
||||||
|
corners[count % 3] = i;
|
||||||
|
count += 1;
|
||||||
|
if count % 3 == 0 {
|
||||||
|
let normal = face_normal(
|
||||||
|
positions[corners[0]],
|
||||||
|
positions[corners[1]],
|
||||||
|
positions[corners[2]],
|
||||||
|
);
|
||||||
|
for corner in corners {
|
||||||
|
normals[corner] =
|
||||||
|
(Vec3::from(normal) + Vec3::from(normals[corner])).into();
|
||||||
|
adjacency_counts[corner] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// average (smooth) normals for shared vertices...
|
||||||
|
// TODO: support different methods of weighting the average
|
||||||
|
for i in 0..normals.len() {
|
||||||
|
let count = adjacency_counts[i];
|
||||||
|
if count > 0 {
|
||||||
|
normals[i] = (Vec3::from(normals[i]) / (count as f32)).normalize().into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let normals: Vec<_> = positions
|
||||||
|
.chunks_exact(3)
|
||||||
|
.map(|p| face_normal(p[0], p[1], p[2]))
|
||||||
|
.flat_map(|normal| [normal; 3])
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
|
/// Consumes the mesh and returns a mesh with calculated [`Mesh::ATTRIBUTE_NORMAL`].
|
||||||
///
|
///
|
||||||
/// (Alternatively, you can use [`Mesh::compute_flat_normals`] to mutate an existing mesh in-place)
|
/// (Alternatively, you can use [`Mesh::with_computed_flat_normals`] to mutate an existing mesh in-place)
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3` or
|
/// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3` or
|
||||||
|
|
Loading…
Reference in a new issue