mirror of
https://github.com/bevyengine/bevy
synced 2025-01-09 19:58:58 +00:00
856b39d821
# Objective - Lazily evaluate [default](https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_or_default)~~/[or](https://rust-lang.github.io/rust-clippy/master/index.html#/or_fun_call)~~ values where it makes sense - ~~`unwrap_or(foo())` -> `unwrap_or_else(|| foo())`~~ - `unwrap_or(Default::default())` -> `unwrap_or_default()` - etc. - Avoid creating [redundant closures](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure), even for [method calls](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure_for_method_calls) - `map(|something| something.into())` -> `map(Into:into)` ## Solution - Apply Clippy lints: - ~~[or_fun_call](https://rust-lang.github.io/rust-clippy/master/index.html#/or_fun_call)~~ - [unwrap_or_default](https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_or_default) - [redundant_closure_for_method_calls](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure_for_method_calls) ([redundant closures](https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_closure) is already enabled) ## Testing - Tested on Windows 11 (`stable-x86_64-pc-windows-gnu`, 1.79.0) - Bevy compiles without errors or warnings and examples seem to work as intended - `cargo clippy` ✅ - `cargo run -p ci -- compile` ✅ --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
305 lines
13 KiB
Rust
305 lines
13 KiB
Rust
use bevy_render::{
|
|
mesh::{MeshVertexAttribute, VertexAttributeValues as Values},
|
|
prelude::Mesh,
|
|
render_resource::VertexFormat,
|
|
};
|
|
use bevy_utils::HashMap;
|
|
use gltf::{
|
|
accessor::{DataType, Dimensions},
|
|
mesh::util::{ReadColors, ReadJoints, ReadTexCoords, ReadWeights},
|
|
};
|
|
use thiserror::Error;
|
|
|
|
/// Represents whether integer data requires normalization
|
|
#[derive(Copy, Clone)]
|
|
struct Normalization(bool);
|
|
|
|
impl Normalization {
|
|
fn apply_either<T, U>(
|
|
self,
|
|
value: T,
|
|
normalized_ctor: impl Fn(T) -> U,
|
|
unnormalized_ctor: impl Fn(T) -> U,
|
|
) -> U {
|
|
if self.0 {
|
|
normalized_ctor(value)
|
|
} else {
|
|
unnormalized_ctor(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An error that occurs when accessing buffer data
|
|
#[derive(Error, Debug)]
|
|
pub(crate) enum AccessFailed {
|
|
#[error("Malformed vertex attribute data")]
|
|
MalformedData,
|
|
#[error("Unsupported vertex attribute format")]
|
|
UnsupportedFormat,
|
|
}
|
|
|
|
/// Helper for reading buffer data
|
|
struct BufferAccessor<'a> {
|
|
accessor: gltf::Accessor<'a>,
|
|
buffer_data: &'a Vec<Vec<u8>>,
|
|
normalization: Normalization,
|
|
}
|
|
|
|
impl<'a> BufferAccessor<'a> {
|
|
/// Creates an iterator over the elements in this accessor
|
|
fn iter<T: gltf::accessor::Item>(self) -> Result<gltf::accessor::Iter<'a, T>, AccessFailed> {
|
|
gltf::accessor::Iter::new(self.accessor, |buffer: gltf::Buffer| {
|
|
self.buffer_data.get(buffer.index()).map(Vec::as_slice)
|
|
})
|
|
.ok_or(AccessFailed::MalformedData)
|
|
}
|
|
|
|
/// Applies the element iterator to a constructor or fails if normalization is required
|
|
fn with_no_norm<T: gltf::accessor::Item, U>(
|
|
self,
|
|
ctor: impl Fn(gltf::accessor::Iter<'a, T>) -> U,
|
|
) -> Result<U, AccessFailed> {
|
|
if self.normalization.0 {
|
|
return Err(AccessFailed::UnsupportedFormat);
|
|
}
|
|
self.iter().map(ctor)
|
|
}
|
|
|
|
/// Applies the element iterator and the normalization flag to a constructor
|
|
fn with_norm<T: gltf::accessor::Item, U>(
|
|
self,
|
|
ctor: impl Fn(gltf::accessor::Iter<'a, T>, Normalization) -> U,
|
|
) -> Result<U, AccessFailed> {
|
|
let normalized = self.normalization;
|
|
self.iter().map(|v| ctor(v, normalized))
|
|
}
|
|
}
|
|
|
|
/// An enum of the iterators user by different vertex attribute formats
|
|
enum VertexAttributeIter<'a> {
|
|
// For reading native WGPU formats
|
|
F32(gltf::accessor::Iter<'a, f32>),
|
|
U32(gltf::accessor::Iter<'a, u32>),
|
|
F32x2(gltf::accessor::Iter<'a, [f32; 2]>),
|
|
U32x2(gltf::accessor::Iter<'a, [u32; 2]>),
|
|
F32x3(gltf::accessor::Iter<'a, [f32; 3]>),
|
|
U32x3(gltf::accessor::Iter<'a, [u32; 3]>),
|
|
F32x4(gltf::accessor::Iter<'a, [f32; 4]>),
|
|
U32x4(gltf::accessor::Iter<'a, [u32; 4]>),
|
|
S16x2(gltf::accessor::Iter<'a, [i16; 2]>, Normalization),
|
|
U16x2(gltf::accessor::Iter<'a, [u16; 2]>, Normalization),
|
|
S16x4(gltf::accessor::Iter<'a, [i16; 4]>, Normalization),
|
|
U16x4(gltf::accessor::Iter<'a, [u16; 4]>, Normalization),
|
|
S8x2(gltf::accessor::Iter<'a, [i8; 2]>, Normalization),
|
|
U8x2(gltf::accessor::Iter<'a, [u8; 2]>, Normalization),
|
|
S8x4(gltf::accessor::Iter<'a, [i8; 4]>, Normalization),
|
|
U8x4(gltf::accessor::Iter<'a, [u8; 4]>, Normalization),
|
|
// Additional on-disk formats used for RGB colors
|
|
U16x3(gltf::accessor::Iter<'a, [u16; 3]>, Normalization),
|
|
U8x3(gltf::accessor::Iter<'a, [u8; 3]>, Normalization),
|
|
}
|
|
|
|
impl<'a> VertexAttributeIter<'a> {
|
|
/// Creates an iterator over the elements in a vertex attribute accessor
|
|
fn from_accessor(
|
|
accessor: gltf::Accessor<'a>,
|
|
buffer_data: &'a Vec<Vec<u8>>,
|
|
) -> Result<VertexAttributeIter<'a>, AccessFailed> {
|
|
let normalization = Normalization(accessor.normalized());
|
|
let format = (accessor.data_type(), accessor.dimensions());
|
|
let acc = BufferAccessor {
|
|
accessor,
|
|
buffer_data,
|
|
normalization,
|
|
};
|
|
match format {
|
|
(DataType::F32, Dimensions::Scalar) => acc.with_no_norm(VertexAttributeIter::F32),
|
|
(DataType::U32, Dimensions::Scalar) => acc.with_no_norm(VertexAttributeIter::U32),
|
|
(DataType::F32, Dimensions::Vec2) => acc.with_no_norm(VertexAttributeIter::F32x2),
|
|
(DataType::U32, Dimensions::Vec2) => acc.with_no_norm(VertexAttributeIter::U32x2),
|
|
(DataType::F32, Dimensions::Vec3) => acc.with_no_norm(VertexAttributeIter::F32x3),
|
|
(DataType::U32, Dimensions::Vec3) => acc.with_no_norm(VertexAttributeIter::U32x3),
|
|
(DataType::F32, Dimensions::Vec4) => acc.with_no_norm(VertexAttributeIter::F32x4),
|
|
(DataType::U32, Dimensions::Vec4) => acc.with_no_norm(VertexAttributeIter::U32x4),
|
|
(DataType::I16, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::S16x2),
|
|
(DataType::U16, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::U16x2),
|
|
(DataType::I16, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::S16x4),
|
|
(DataType::U16, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::U16x4),
|
|
(DataType::I8, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::S8x2),
|
|
(DataType::U8, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::U8x2),
|
|
(DataType::I8, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::S8x4),
|
|
(DataType::U8, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::U8x4),
|
|
(DataType::U16, Dimensions::Vec3) => acc.with_norm(VertexAttributeIter::U16x3),
|
|
(DataType::U8, Dimensions::Vec3) => acc.with_norm(VertexAttributeIter::U8x3),
|
|
_ => Err(AccessFailed::UnsupportedFormat),
|
|
}
|
|
}
|
|
|
|
/// Materializes values for any supported format of vertex attribute
|
|
fn into_any_values(self) -> Result<Values, AccessFailed> {
|
|
match self {
|
|
VertexAttributeIter::F32(it) => Ok(Values::Float32(it.collect())),
|
|
VertexAttributeIter::U32(it) => Ok(Values::Uint32(it.collect())),
|
|
VertexAttributeIter::F32x2(it) => Ok(Values::Float32x2(it.collect())),
|
|
VertexAttributeIter::U32x2(it) => Ok(Values::Uint32x2(it.collect())),
|
|
VertexAttributeIter::F32x3(it) => Ok(Values::Float32x3(it.collect())),
|
|
VertexAttributeIter::U32x3(it) => Ok(Values::Uint32x3(it.collect())),
|
|
VertexAttributeIter::F32x4(it) => Ok(Values::Float32x4(it.collect())),
|
|
VertexAttributeIter::U32x4(it) => Ok(Values::Uint32x4(it.collect())),
|
|
VertexAttributeIter::S16x2(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Snorm16x2, Values::Sint16x2))
|
|
}
|
|
VertexAttributeIter::U16x2(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Unorm16x2, Values::Uint16x2))
|
|
}
|
|
VertexAttributeIter::S16x4(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Snorm16x4, Values::Sint16x4))
|
|
}
|
|
VertexAttributeIter::U16x4(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Unorm16x4, Values::Uint16x4))
|
|
}
|
|
VertexAttributeIter::S8x2(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Snorm8x2, Values::Sint8x2))
|
|
}
|
|
VertexAttributeIter::U8x2(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Unorm8x2, Values::Uint8x2))
|
|
}
|
|
VertexAttributeIter::S8x4(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Snorm8x4, Values::Sint8x4))
|
|
}
|
|
VertexAttributeIter::U8x4(it, n) => {
|
|
Ok(n.apply_either(it.collect(), Values::Unorm8x4, Values::Uint8x4))
|
|
}
|
|
_ => Err(AccessFailed::UnsupportedFormat),
|
|
}
|
|
}
|
|
|
|
/// Materializes RGBA values, converting compatible formats to Float32x4
|
|
fn into_rgba_values(self) -> Result<Values, AccessFailed> {
|
|
match self {
|
|
VertexAttributeIter::U8x3(it, Normalization(true)) => Ok(Values::Float32x4(
|
|
ReadColors::RgbU8(it).into_rgba_f32().collect(),
|
|
)),
|
|
VertexAttributeIter::U16x3(it, Normalization(true)) => Ok(Values::Float32x4(
|
|
ReadColors::RgbU16(it).into_rgba_f32().collect(),
|
|
)),
|
|
VertexAttributeIter::F32x3(it) => Ok(Values::Float32x4(
|
|
ReadColors::RgbF32(it).into_rgba_f32().collect(),
|
|
)),
|
|
VertexAttributeIter::U8x4(it, Normalization(true)) => Ok(Values::Float32x4(
|
|
ReadColors::RgbaU8(it).into_rgba_f32().collect(),
|
|
)),
|
|
VertexAttributeIter::U16x4(it, Normalization(true)) => Ok(Values::Float32x4(
|
|
ReadColors::RgbaU16(it).into_rgba_f32().collect(),
|
|
)),
|
|
s => s.into_any_values(),
|
|
}
|
|
}
|
|
|
|
/// Materializes joint index values, converting compatible formats to Uint16x4
|
|
fn into_joint_index_values(self) -> Result<Values, AccessFailed> {
|
|
match self {
|
|
VertexAttributeIter::U8x4(it, Normalization(false)) => {
|
|
Ok(Values::Uint16x4(ReadJoints::U8(it).into_u16().collect()))
|
|
}
|
|
s => s.into_any_values(),
|
|
}
|
|
}
|
|
|
|
/// Materializes joint weight values, converting compatible formats to Float32x4
|
|
fn into_joint_weight_values(self) -> Result<Values, AccessFailed> {
|
|
match self {
|
|
VertexAttributeIter::U8x4(it, Normalization(true)) => {
|
|
Ok(Values::Float32x4(ReadWeights::U8(it).into_f32().collect()))
|
|
}
|
|
VertexAttributeIter::U16x4(it, Normalization(true)) => {
|
|
Ok(Values::Float32x4(ReadWeights::U16(it).into_f32().collect()))
|
|
}
|
|
s => s.into_any_values(),
|
|
}
|
|
}
|
|
|
|
/// Materializes texture coordinate values, converting compatible formats to Float32x2
|
|
fn into_tex_coord_values(self) -> Result<Values, AccessFailed> {
|
|
match self {
|
|
VertexAttributeIter::U8x2(it, Normalization(true)) => Ok(Values::Float32x2(
|
|
ReadTexCoords::U8(it).into_f32().collect(),
|
|
)),
|
|
VertexAttributeIter::U16x2(it, Normalization(true)) => Ok(Values::Float32x2(
|
|
ReadTexCoords::U16(it).into_f32().collect(),
|
|
)),
|
|
s => s.into_any_values(),
|
|
}
|
|
}
|
|
}
|
|
|
|
enum ConversionMode {
|
|
Any,
|
|
Rgba,
|
|
JointIndex,
|
|
JointWeight,
|
|
TexCoord,
|
|
}
|
|
|
|
#[derive(Error, Debug)]
|
|
pub(crate) enum ConvertAttributeError {
|
|
#[error("Vertex attribute {0} has format {1:?} but expected {3:?} for target attribute {2}")]
|
|
WrongFormat(String, VertexFormat, String, VertexFormat),
|
|
#[error("{0} in accessor {1}")]
|
|
AccessFailed(AccessFailed, usize),
|
|
#[error("Unknown vertex attribute {0}")]
|
|
UnknownName(String),
|
|
}
|
|
|
|
pub(crate) fn convert_attribute(
|
|
semantic: gltf::Semantic,
|
|
accessor: gltf::Accessor,
|
|
buffer_data: &Vec<Vec<u8>>,
|
|
custom_vertex_attributes: &HashMap<Box<str>, MeshVertexAttribute>,
|
|
) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> {
|
|
if let Some((attribute, conversion)) = match &semantic {
|
|
gltf::Semantic::Positions => Some((Mesh::ATTRIBUTE_POSITION, ConversionMode::Any)),
|
|
gltf::Semantic::Normals => Some((Mesh::ATTRIBUTE_NORMAL, ConversionMode::Any)),
|
|
gltf::Semantic::Tangents => Some((Mesh::ATTRIBUTE_TANGENT, ConversionMode::Any)),
|
|
gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba)),
|
|
gltf::Semantic::TexCoords(0) => Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord)),
|
|
gltf::Semantic::TexCoords(1) => Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord)),
|
|
gltf::Semantic::Joints(0) => {
|
|
Some((Mesh::ATTRIBUTE_JOINT_INDEX, ConversionMode::JointIndex))
|
|
}
|
|
gltf::Semantic::Weights(0) => {
|
|
Some((Mesh::ATTRIBUTE_JOINT_WEIGHT, ConversionMode::JointWeight))
|
|
}
|
|
gltf::Semantic::Extras(name) => custom_vertex_attributes
|
|
.get(name.as_str())
|
|
.map(|attr| (attr.clone(), ConversionMode::Any)),
|
|
_ => None,
|
|
} {
|
|
let raw_iter = VertexAttributeIter::from_accessor(accessor.clone(), buffer_data);
|
|
let converted_values = raw_iter.and_then(|iter| match conversion {
|
|
ConversionMode::Any => iter.into_any_values(),
|
|
ConversionMode::Rgba => iter.into_rgba_values(),
|
|
ConversionMode::TexCoord => iter.into_tex_coord_values(),
|
|
ConversionMode::JointIndex => iter.into_joint_index_values(),
|
|
ConversionMode::JointWeight => iter.into_joint_weight_values(),
|
|
});
|
|
match converted_values {
|
|
Ok(values) => {
|
|
let loaded_format = VertexFormat::from(&values);
|
|
if attribute.format == loaded_format {
|
|
Ok((attribute, values))
|
|
} else {
|
|
Err(ConvertAttributeError::WrongFormat(
|
|
semantic.to_string(),
|
|
loaded_format,
|
|
attribute.name.to_string(),
|
|
attribute.format,
|
|
))
|
|
}
|
|
}
|
|
Err(err) => Err(ConvertAttributeError::AccessFailed(err, accessor.index())),
|
|
}
|
|
} else {
|
|
Err(ConvertAttributeError::UnknownName(semantic.to_string()))
|
|
}
|
|
}
|