Add invert_winding
for triangle list meshes (#14555)
# Objective Implements #14547 ## Solution Add a function `invert_winding` for `Mesh` that inverts the winding for `LineList`, `LineStrip`, `TriangleList` and `TriangleStrip`. ## Testing Tests added --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Alix Bott <bott.alix@gmail.com>
This commit is contained in:
parent
5f2570eb4c
commit
0d0f77a7ab
@ -537,7 +537,7 @@ impl Mesh {
|
|||||||
/// Consumes the mesh and returns a mesh with no shared vertices.
|
/// Consumes the mesh and returns a mesh with no shared vertices.
|
||||||
///
|
///
|
||||||
/// This can dramatically increase the vertex count, so make sure this is what you want.
|
/// This can dramatically increase the vertex count, so make sure this is what you want.
|
||||||
/// Does nothing if no [Indices] are set.
|
/// Does nothing if no [`Indices`] are set.
|
||||||
///
|
///
|
||||||
/// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place)
|
/// (Alternatively, you can use [`Mesh::duplicate_vertices`] to mutate an existing mesh in-place)
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -546,6 +546,62 @@ impl Mesh {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inverts the winding of the indices such that all counter-clockwise triangles are now
|
||||||
|
/// clockwise and vice versa.
|
||||||
|
/// For lines, their start and end indices are flipped.
|
||||||
|
///
|
||||||
|
/// Does nothing if no [`Indices`] are set.
|
||||||
|
/// If this operation succeeded, an [`Ok`] result is returned.
|
||||||
|
pub fn invert_winding(&mut self) -> Result<(), MeshWindingInvertError> {
|
||||||
|
fn invert<I>(
|
||||||
|
indices: &mut [I],
|
||||||
|
topology: PrimitiveTopology,
|
||||||
|
) -> Result<(), MeshWindingInvertError> {
|
||||||
|
match topology {
|
||||||
|
PrimitiveTopology::TriangleList => {
|
||||||
|
// Early return if the index count doesn't match
|
||||||
|
if indices.len() % 3 != 0 {
|
||||||
|
return Err(MeshWindingInvertError::AbruptIndicesEnd);
|
||||||
|
}
|
||||||
|
for chunk in indices.chunks_mut(3) {
|
||||||
|
// This currently can only be optimized away with unsafe, rework this when `feature(slice_as_chunks)` gets stable.
|
||||||
|
let [_, b, c] = chunk else {
|
||||||
|
return Err(MeshWindingInvertError::AbruptIndicesEnd);
|
||||||
|
};
|
||||||
|
std::mem::swap(b, c);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
PrimitiveTopology::LineList => {
|
||||||
|
// Early return if the index count doesn't match
|
||||||
|
if indices.len() % 2 != 0 {
|
||||||
|
return Err(MeshWindingInvertError::AbruptIndicesEnd);
|
||||||
|
}
|
||||||
|
indices.reverse();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
PrimitiveTopology::TriangleStrip | PrimitiveTopology::LineStrip => {
|
||||||
|
indices.reverse();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(MeshWindingInvertError::WrongTopology),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &mut self.indices {
|
||||||
|
Some(Indices::U16(vec)) => invert(vec, self.primitive_topology),
|
||||||
|
Some(Indices::U32(vec)) => invert(vec, self.primitive_topology),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the mesh and returns a mesh with inverted winding of the indices such
|
||||||
|
/// that all counter-clockwise triangles are now clockwise and vice versa.
|
||||||
|
///
|
||||||
|
/// Does nothing if no [`Indices`] are set.
|
||||||
|
pub fn with_inverted_winding(mut self) -> Result<Self, MeshWindingInvertError> {
|
||||||
|
self.invert_winding().map(|_| self)
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
|
/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh.
|
||||||
/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
|
/// If the mesh is indexed, this defaults to smooth normals. Otherwise, it defaults to flat
|
||||||
/// normals.
|
/// normals.
|
||||||
@ -1183,6 +1239,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error that occurred while trying to invert the winding of a [`Mesh`].
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum MeshWindingInvertError {
|
||||||
|
/// This error occurs when you try to invert the winding for a mesh with [`PrimitiveTopology::PointList`].
|
||||||
|
#[error("Mesh winding invertation does not work for primitive topology `PointList`")]
|
||||||
|
WrongTopology,
|
||||||
|
|
||||||
|
/// This error occurs when you try to invert the winding for a mesh with
|
||||||
|
/// * [`PrimitiveTopology::TriangleList`], but the indices are not in chunks of 3.
|
||||||
|
/// * [`PrimitiveTopology::LineList`], but the indices are not in chunks of 2.
|
||||||
|
#[error("Indices weren't in chunks according to topology")]
|
||||||
|
AbruptIndicesEnd,
|
||||||
|
}
|
||||||
|
|
||||||
/// An error that occurred while trying to extract a collection of triangles from a [`Mesh`].
|
/// An error that occurred while trying to extract a collection of triangles from a [`Mesh`].
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum MeshTrianglesError {
|
pub enum MeshTrianglesError {
|
||||||
@ -1903,7 +1973,10 @@ fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Mesh;
|
use super::Mesh;
|
||||||
use crate::{mesh::VertexAttributeValues, render_asset::RenderAssetUsages};
|
use crate::{
|
||||||
|
mesh::{Indices, MeshWindingInvertError, VertexAttributeValues},
|
||||||
|
render_asset::RenderAssetUsages,
|
||||||
|
};
|
||||||
use bevy_math::Vec3;
|
use bevy_math::Vec3;
|
||||||
use bevy_transform::components::Transform;
|
use bevy_transform::components::Transform;
|
||||||
use wgpu::PrimitiveTopology;
|
use wgpu::PrimitiveTopology;
|
||||||
@ -1969,4 +2042,93 @@ mod tests {
|
|||||||
panic!("Mesh does not have a uv attribute");
|
panic!("Mesh does not have a uv attribute");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn point_list_mesh_invert_winding() {
|
||||||
|
let mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::default())
|
||||||
|
.with_inserted_indices(Indices::U32(vec![]));
|
||||||
|
assert!(matches!(
|
||||||
|
mesh.with_inverted_winding(),
|
||||||
|
Err(MeshWindingInvertError::WrongTopology)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn line_list_mesh_invert_winding() {
|
||||||
|
let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
|
||||||
|
.with_inserted_indices(Indices::U32(vec![0, 1, 1, 2, 2, 3]));
|
||||||
|
let mesh = mesh.with_inverted_winding().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
|
||||||
|
vec![3, 2, 2, 1, 1, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn line_list_mesh_invert_winding_fail() {
|
||||||
|
let mesh = Mesh::new(PrimitiveTopology::LineList, RenderAssetUsages::default())
|
||||||
|
.with_inserted_indices(Indices::U32(vec![0, 1, 1]));
|
||||||
|
assert!(matches!(
|
||||||
|
mesh.with_inverted_winding(),
|
||||||
|
Err(MeshWindingInvertError::AbruptIndicesEnd)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn line_strip_mesh_invert_winding() {
|
||||||
|
let mesh = Mesh::new(PrimitiveTopology::LineStrip, RenderAssetUsages::default())
|
||||||
|
.with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
|
||||||
|
let mesh = mesh.with_inverted_winding().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
|
||||||
|
vec![3, 2, 1, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn triangle_list_mesh_invert_winding() {
|
||||||
|
let mesh = Mesh::new(
|
||||||
|
PrimitiveTopology::TriangleList,
|
||||||
|
RenderAssetUsages::default(),
|
||||||
|
)
|
||||||
|
.with_inserted_indices(Indices::U32(vec![
|
||||||
|
0, 3, 1, // First triangle
|
||||||
|
1, 3, 2, // Second triangle
|
||||||
|
]));
|
||||||
|
let mesh = mesh.with_inverted_winding().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
|
||||||
|
vec![
|
||||||
|
0, 1, 3, // First triangle
|
||||||
|
1, 2, 3, // Second triangle
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn triangle_list_mesh_invert_winding_fail() {
|
||||||
|
let mesh = Mesh::new(
|
||||||
|
PrimitiveTopology::TriangleList,
|
||||||
|
RenderAssetUsages::default(),
|
||||||
|
)
|
||||||
|
.with_inserted_indices(Indices::U32(vec![0, 3, 1, 2]));
|
||||||
|
assert!(matches!(
|
||||||
|
mesh.with_inverted_winding(),
|
||||||
|
Err(MeshWindingInvertError::AbruptIndicesEnd)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn triangle_strip_mesh_invert_winding() {
|
||||||
|
let mesh = Mesh::new(
|
||||||
|
PrimitiveTopology::TriangleStrip,
|
||||||
|
RenderAssetUsages::default(),
|
||||||
|
)
|
||||||
|
.with_inserted_indices(Indices::U32(vec![0, 1, 2, 3]));
|
||||||
|
let mesh = mesh.with_inverted_winding().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
mesh.indices().unwrap().iter().collect::<Vec<usize>>(),
|
||||||
|
vec![3, 2, 1, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user