diff options
Diffstat (limited to 'engine')
37 files changed, 1076 insertions, 887 deletions
diff --git a/engine/src/asset.rs b/engine/src/asset.rs index dd8aa37..56b8a46 100644 --- a/engine/src/asset.rs +++ b/engine/src/asset.rs @@ -38,11 +38,12 @@ pub struct Label<'a> impl Label<'_> { + #[must_use] pub fn to_owned(&self) -> LabelOwned { LabelOwned { path: self.path.to_path_buf(), - name: self.name.as_ref().map(|name| name.to_string()), + name: self.name.as_ref().map(std::string::ToString::to_string), } } } @@ -82,7 +83,7 @@ impl Display for Label<'_> if let Some(name) = &self.name { formatter.write_str("::")?; - formatter.write_str(&name)?; + formatter.write_str(name)?; } Ok(()) @@ -98,6 +99,7 @@ pub struct LabelOwned impl LabelOwned { + #[must_use] pub fn to_label(&self) -> Label<'_> { Label { @@ -115,7 +117,7 @@ impl Display for LabelOwned if let Some(name) = &self.name { formatter.write_str("::")?; - formatter.write_str(&name)?; + formatter.write_str(name)?; } Ok(()) @@ -125,7 +127,7 @@ impl Display for LabelOwned #[derive(Debug, Sole)] pub struct Assets { - assets: Vec<StoredAsset>, + store: Vec<StoredAsset>, asset_lookup: RefCell<HashMap<LabelHash, LookupEntry>>, importers: Vec<WrappedImporterFn>, importer_lookup: HashMap<OsString, usize>, @@ -137,13 +139,14 @@ pub struct Assets impl Assets { + #[must_use] pub fn with_capacity(capacity: usize) -> Self { let (import_work_msg_sender, import_work_msg_receiver) = mpsc_channel::<ImportWorkMessage>(); Self { - assets: Vec::with_capacity(capacity), + store: Vec::with_capacity(capacity), asset_lookup: RefCell::new(HashMap::with_capacity(capacity)), importers: Vec::new(), importer_lookup: HashMap::new(), @@ -194,7 +197,9 @@ impl Assets tracing::Span::current() .record("asset_label", tracing::field::display(&asset_label)); - let stored_asset = self.assets.get(*asset_index).expect("Not possible"); + let Some(stored_asset) = self.store.get(*asset_index) else { + unreachable!(); + }; let Some(asset) = stored_asset.strong.downcast_ref::<Asset>() else { tracing::error!("Wrong asset type"); @@ -220,12 +225,14 @@ impl Assets return None; }; - let stored_asset = self.assets.get(*asset_index).expect("Not possible"); + let Some(stored_asset) = self.store.get(*asset_index) else { + unreachable!(); + }; if stored_asset.strong.downcast_ref::<Asset>().is_none() { tracing::error!("Wrong asset type"); return None; - }; + } Some(Handle::new(label_hash)) } @@ -243,7 +250,9 @@ impl Assets return false; }; - let stored_asset = self.assets.get(*asset_index).expect("Not possible"); + let Some(stored_asset) = self.store.get(*asset_index) else { + unreachable!(); + }; stored_asset.strong.downcast_ref::<Asset>().is_some() } @@ -306,7 +315,9 @@ impl Assets match lookup_entry { LookupEntry::Occupied(asset_index, _) => { - let stored_asset = self.assets.get(*asset_index).expect("Not possible"); + let Some(stored_asset) = self.store.get(*asset_index) else { + unreachable!(); + }; if stored_asset.strong.downcast_ref::<Asset>().is_none() { tracing::error!("Wrong asset type {}", type_name::<Asset>()); @@ -351,7 +362,9 @@ impl Assets match lookup_entry { LookupEntry::Occupied(asset_index, _) => { - let stored_asset = self.assets.get(*asset_index).expect("Not possible"); + let Some(stored_asset) = self.store.get(*asset_index) else { + unreachable!(); + }; if stored_asset.strong.downcast_ref::<Asset>().is_none() { tracing::error!( @@ -420,9 +433,9 @@ impl Assets tracing::debug!("Storing asset"); - self.assets.push(StoredAsset::new(asset)); + self.store.push(StoredAsset::new(asset)); - let index = self.assets.len() - 1; + let index = self.store.len() - 1; self.asset_lookup .get_mut() @@ -455,7 +468,7 @@ impl Assets if let Some(LookupEntry::Pending) = asset_lookup.get(&LabelHash::new(label)) { return true; - }; + } false } @@ -528,7 +541,11 @@ impl Assets { let index = *self.importer_lookup.get(file_ext)?; - Some(self.importers.get(index).expect("Not possible")) + let Some(importer) = self.importers.get(index) else { + unreachable!(); + }; + + Some(importer) } } @@ -660,11 +677,13 @@ pub struct Handle<Asset: 'static> impl<Asset: 'static> Handle<Asset> { + #[must_use] pub fn from_id(id: Id) -> Self { Self { id, _pd: PhantomData } } + #[must_use] pub fn id(&self) -> Id { self.id @@ -838,17 +857,17 @@ fn add_received_assets(mut assets: Single<Assets>) curr_tick_events.clear(); - while let Some(import_work_msg) = assets.import_work_msg_receiver.try_recv().ok() { + while let Ok(import_work_msg) = assets.import_work_msg_receiver.try_recv() { match import_work_msg { ImportWorkMessage::Store { do_store, label, asset } => { do_store(assets, label, asset); } ImportWorkMessage::Load { do_load, label, asset_settings } => { do_load( - &assets, + assets, Label { path: label.path.as_path().into(), - name: label.name.as_deref().map(|name| name.into()), + name: label.name.as_deref().map(std::convert::Into::into), }, asset_settings, ); diff --git a/engine/src/camera.rs b/engine/src/camera.rs index 51cd54c..dfccbd7 100644 --- a/engine/src/camera.rs +++ b/engine/src/camera.rs @@ -16,6 +16,7 @@ pub struct Camera impl Camera { + #[must_use] pub fn to_view_matrix(&self, camera_world_pos: Vec3<f32>) -> Matrix<f32, 4, 4> { let mut view = Matrix::new(); diff --git a/engine/src/collision.rs b/engine/src/collision.rs index 015ab58..3808754 100644 --- a/engine/src/collision.rs +++ b/engine/src/collision.rs @@ -18,6 +18,7 @@ pub struct BoxCollider impl BoxCollider { + #[must_use] pub fn for_mesh(mesh: &Mesh) -> Self { let furthest_dir_points = mesh.find_furthest_vertex_positions(); @@ -36,6 +37,7 @@ impl BoxCollider } } + #[must_use] pub fn offset(self, offset: Vec3<f32>) -> Self { Self { @@ -88,6 +90,7 @@ pub struct SphereCollider impl SphereCollider { + #[must_use] pub fn offset(self, offset: Vec3<f32>) -> Self { Self { diff --git a/engine/src/data_types/matrix.rs b/engine/src/data_types/matrix.rs index 9ab5bd4..149f0dc 100644 --- a/engine/src/data_types/matrix.rs +++ b/engine/src/data_types/matrix.rs @@ -43,7 +43,7 @@ impl<Value, const ROWS: usize, const COLUMNS: usize> Matrix<Value, ROWS, COLUMNS where Row: Into<[Value; COLUMNS]>, { - let items = rows.map(|row| row.into()); + let items = rows.map(Into::into); Self { items } } @@ -59,7 +59,7 @@ impl<Value, const ROWS: usize, const COLUMNS: usize> Matrix<Value, ROWS, COLUMNS pub fn items(&self) -> &[Value] { - &self.items.as_flattened() + self.items.as_flattened() } pub fn to_column_major(&self) -> [[Value; ROWS]; COLUMNS] @@ -160,6 +160,7 @@ impl Matrix<f32, 4, 4> self[CellPos { row: 3, col: 3 }] = 1.0; } + #[must_use] pub fn inverse(&self) -> Self { let coef_00 = self[CellPos { row: 2, col: 2 }] * self[CellPos { row: 3, col: 3 }] @@ -411,7 +412,7 @@ where fn mul(self, rhs: Vec3<Value>) -> Self::Output { - self * &rhs + <Self as Mul<&Vec3<Value>>>::mul(self, &rhs) } } @@ -463,7 +464,7 @@ where fn mul(self, rhs: Vec4<Value>) -> Self::Output { - self * &rhs + <Self as Mul<&Vec4<Value>>>::mul(self, &rhs) } } diff --git a/engine/src/data_types/vector.rs b/engine/src/data_types/vector.rs index 915599a..106f215 100644 --- a/engine/src/data_types/vector.rs +++ b/engine/src/data_types/vector.rs @@ -187,6 +187,7 @@ impl Vec3<f32> .normalize() } + #[must_use] pub fn into_deg_angles(self) -> Angles<f32> { let Self { x, y, z } = self.normalize(); diff --git a/engine/src/file_format/wavefront/obj.rs b/engine/src/file_format/wavefront/obj.rs index dbcee33..d1a1bf3 100644 --- a/engine/src/file_format/wavefront/obj.rs +++ b/engine/src/file_format/wavefront/obj.rs @@ -43,7 +43,7 @@ pub fn parse(obj_content: &str) -> Result<Obj, Error> continue; } - let Some((keyword, _)) = line.split_once(" ") else { + let Some((keyword, _)) = line.split_once(' ') else { continue; }; @@ -119,19 +119,18 @@ impl Obj for face in &self.faces { for face_vertex in &face.vertices { - if let Some(index) = added_face_vertices.get(&face_vertex) { + if let Some(index) = added_face_vertices.get(face_vertex) { indices.push(*index); continue; } - let pos = self + let pos = *self .vertex_positions .get(face_vertex.position as usize - 1) .ok_or(Error::FaceVertexPositionNotFound { vertex_pos_index: face_vertex.position, - })? - .clone(); + })?; let texture_pos = face_vertex.texture.map_or_else( || { @@ -154,10 +153,11 @@ impl Obj }, )?; - let texture_pos = options - .y_flip_uvs - .then(|| Vec2 { x: texture_pos.x, y: -texture_pos.y }) - .unwrap_or(texture_pos); + let texture_pos = if options.y_flip_uvs { + Vec2 { x: texture_pos.x, y: -texture_pos.y } + } else { + texture_pos + }; let normal = face_vertex.normal.map_or_else( || { @@ -243,14 +243,12 @@ impl Obj fn with_capacities_from_item_cnts(item_counts: ItemCounts) -> Self { Self { - vertex_positions: Vec::with_capacity(item_counts.pos_cnt), - vertex_normals: Vec::with_capacity(item_counts.normal_cnt), - texture_positions: Vec::with_capacity(item_counts.uv_cnt), - faces: Vec::with_capacity(item_counts.face_cnt), - mtl_libs: Vec::with_capacity(item_counts.mtl_lib_cnt), - unique_used_material_names: Vec::with_capacity( - item_counts.material_usage_cnt, - ), + vertex_positions: Vec::with_capacity(item_counts.pos), + vertex_normals: Vec::with_capacity(item_counts.normal), + texture_positions: Vec::with_capacity(item_counts.uv), + faces: Vec::with_capacity(item_counts.face), + mtl_libs: Vec::with_capacity(item_counts.mtl_lib), + unique_used_material_names: Vec::with_capacity(item_counts.material_usage), } } @@ -286,7 +284,7 @@ impl Obj return Err(Error::UnsupportedArgumentCount { keyword: statement.keyword.to_string(), arg_count: statement.arguments.len(), - line_no: line_no, + line_no, }); } @@ -294,7 +292,7 @@ impl Obj return Err(Error::InvalidArgumentCount { keyword: statement.keyword.to_string(), arg_count: statement.arguments.len(), - line_no: line_no, + line_no, }); } @@ -319,7 +317,7 @@ impl Obj return Err(Error::UnsupportedArgumentCount { keyword: statement.keyword.to_string(), arg_count: statement.arguments.len(), - line_no: line_no, + line_no, }); } @@ -327,7 +325,7 @@ impl Obj return Err(Error::InvalidArgumentCount { keyword: statement.keyword.to_string(), arg_count: statement.arguments.len(), - line_no: line_no, + line_no, }); } @@ -351,7 +349,7 @@ impl Obj return Err(Error::InvalidArgumentCount { keyword: statement.keyword.to_string(), arg_count: statement.arguments.len(), - line_no: line_no, + line_no, }); } @@ -377,7 +375,7 @@ impl Obj return Err(Error::InvalidArgumentCount { keyword: statement.keyword.to_string(), arg_count: statement.arguments.len(), - line_no: line_no, + line_no, }); } @@ -408,7 +406,7 @@ impl Obj return Err(Error::UnsupportedArgumentCount { keyword: statement.keyword.to_string(), arg_count: statement.arguments.len(), - line_no: line_no, + line_no, }); } @@ -459,6 +457,7 @@ impl ToMeshOptions /// their origin at the bottom left corner of the image /// /// The default is `true`. + #[must_use] pub fn y_flip_uvs(mut self, y_flip_uvs: bool) -> Self { self.y_flip_uvs = y_flip_uvs; @@ -578,12 +577,12 @@ struct ParsingState #[derive(Debug, Default)] struct ItemCounts { - pos_cnt: usize, - uv_cnt: usize, - normal_cnt: usize, - face_cnt: usize, - mtl_lib_cnt: usize, - material_usage_cnt: usize, + pos: usize, + uv: usize, + normal: usize, + face: usize, + mtl_lib: usize, + material_usage: usize, } impl ItemCounts @@ -591,12 +590,12 @@ impl ItemCounts fn increment_item_cnt_by_keyword(&mut self, keyword: Keyword) { match keyword { - Keyword::V => self.pos_cnt += 1, - Keyword::Vn => self.normal_cnt += 1, - Keyword::Vt => self.uv_cnt += 1, - Keyword::F => self.face_cnt += 1, - Keyword::Mtllib => self.mtl_lib_cnt += 1, - Keyword::Usemtl => self.material_usage_cnt += 1, + Keyword::V => self.pos += 1, + Keyword::Vn => self.normal += 1, + Keyword::Vt => self.uv += 1, + Keyword::F => self.face += 1, + Keyword::Mtllib => self.mtl_lib += 1, + Keyword::Usemtl => self.material_usage += 1, Keyword::O | Keyword::S => {} } } diff --git a/engine/src/image.rs b/engine/src/image.rs index b01ea53..fd6629e 100644 --- a/engine/src/image.rs +++ b/engine/src/image.rs @@ -70,22 +70,25 @@ impl Image Self::from((color, size)) } + #[must_use] pub fn dimensions(&self) -> Dimens<u32> { self.inner.dimensions().into() } + #[must_use] pub fn color_type(&self) -> ColorType { self.inner.color().into() } + #[must_use] pub fn color_space_is_srgb(&self) -> bool { - match self.inner.color_space().primaries { - image_rs::metadata::CicpColorPrimaries::SRgb => true, - _ => false, - } + matches!( + self.inner.color_space().primaries, + image_rs::metadata::CicpColorPrimaries::SRgb + ) } /// Returns a immutable view of a subsection of this image. @@ -93,14 +96,16 @@ impl Image /// /// # Panics /// Panics if the view would be out of bounds + #[must_use] pub fn sub_view(&self, offset: Vec2<u32>, size: Dimens<u32>) -> SubView<'_> { assert!(offset.x + size.width <= self.dimensions().width); assert!(offset.y + size.height <= self.dimensions().height); - SubView { image: &self, offset, size } + SubView { image: self, offset, size } } + #[must_use] pub fn to_rgba8(&self) -> Self { Self { inner: self.inner.to_rgba8().into() } @@ -108,6 +113,7 @@ impl Image /// Consumes the image and returns a RGBA8 image. If it already is RGBA8, the image is /// returned as is. If not, the image is converted to RGBA8. + #[must_use] pub fn into_rgba8(self) -> Self { Self { @@ -115,11 +121,13 @@ impl Image } } + #[must_use] pub fn as_bytes(&self) -> &[u8] { self.inner.as_bytes() } + #[must_use] pub fn into_bytes(self) -> Vec<u8> { self.inner.into_bytes() @@ -303,6 +311,7 @@ pub struct SubView<'image> impl SubView<'_> { + #[must_use] pub fn to_image(&self) -> Image { fn create_sub_image<Image>( diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 36affff..57aa125 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -1,5 +1,10 @@ #![deny(clippy::all, clippy::pedantic)] -#![allow(clippy::needless_pass_by_value)] +#![allow( + clippy::needless_pass_by_value, + clippy::struct_excessive_bools, + clippy::type_complexity, + clippy::too_many_arguments +)] use crate::asset::{Assets, Extension as AssetExtension}; use crate::ecs::error::HandlerFn as ErrorHandlerFn; @@ -67,12 +72,14 @@ impl Engine Self { world } } + #[must_use] pub fn with_error_handler(mut self, error_handler: ErrorHandlerFn) -> Self { self.world.set_err_handler(error_handler); self } + #[must_use] pub fn with_extension(mut self, extension: impl Extension) -> Self { self.world.add_extension(extension); diff --git a/engine/src/lighting.rs b/engine/src/lighting.rs index 5314e58..8b75de0 100644 --- a/engine/src/lighting.rs +++ b/engine/src/lighting.rs @@ -122,6 +122,7 @@ pub struct Environmental impl Environmental { + #[must_use] pub fn builder() -> EnvironmentalBuilder { EnvironmentalBuilder::default() diff --git a/engine/src/material.rs b/engine/src/material.rs index af982f4..fc9fcd7 100644 --- a/engine/src/material.rs +++ b/engine/src/material.rs @@ -22,6 +22,7 @@ pub struct Material impl Material { + #[must_use] pub fn builder() -> Builder { Builder::new() @@ -187,6 +188,7 @@ impl Default for Flags impl FlagsBuilder { + #[must_use] pub const fn new() -> Self { Self { use_ambient_color: false } diff --git a/engine/src/mesh.rs b/engine/src/mesh.rs index c1c0f77..771f7bd 100644 --- a/engine/src/mesh.rs +++ b/engine/src/mesh.rs @@ -17,13 +17,26 @@ pub struct Mesh impl Mesh { + #[must_use] pub fn cube(size: Dimens3<f32>) -> Self { Self::cube_with_options(CubeOptions::default().size(size)) } + #[must_use] pub fn cube_with_options(options: CubeOptions) -> Self { + #[inline] + fn vertex(pos: [f32; 3], uv: [f32; 2], normal: Vec3<f32>) -> [f32; 8] + { + let [pos_x, pos_y, pos_z] = pos; + let [uv_x, uv_y] = uv; + + [ + pos_x, pos_y, pos_z, uv_x, uv_y, normal.x, normal.y, normal.z, + ] + } + let CubeOptions { size, top_face: @@ -62,17 +75,6 @@ impl Mesh let half_h = size.height / 2.0; let half_d = size.depth / 2.0; - #[inline(always)] - fn vertex(pos: [f32; 3], uv: [f32; 2], normal: Vec3<f32>) -> [f32; 8] - { - let [pos_x, pos_y, pos_z] = pos; - let [uv_x, uv_y] = uv; - - [ - pos_x, pos_y, pos_z, uv_x, uv_y, normal.x, normal.y, normal.z, - ] - } - let top_vertices = [ ( [half_w, half_h, half_d], @@ -235,11 +237,13 @@ impl Mesh .build() } + #[must_use] pub fn builder() -> Builder { Builder::default() } + #[must_use] pub fn vertex_buf(&self) -> &vertex_buffer::VertexBuffer { &self.vertex_buf @@ -264,22 +268,22 @@ impl Mesh pub fn set_indices(&mut self, indices: impl IntoIterator<Item = u32>) { - let curr_indices = self.indices.get_or_insert_with(|| Vec::new()); + let curr_indices = self.indices.get_or_insert_with(Vec::new); curr_indices.clear(); - curr_indices.extend(indices.into_iter()); + curr_indices.extend(indices); } /// Finds the vertex positions that are furthest in every 3D direction. Keep in mind /// that this can be quite time-expensive if the mesh has many vertices. + #[must_use] pub fn find_furthest_vertex_positions(&self) -> DirectionPositions { let mut pos_iter = self .vertex_buf() .iter::<[f32; 3]>(VertexLabel::Position) - .map(|vertex_pos| Vec3::from(*vertex_pos)) - .into_iter(); + .map(|vertex_pos| Vec3::from(*vertex_pos)); let first_pos = pos_iter.next().unwrap(); @@ -318,23 +322,27 @@ pub struct Builder impl Builder { + #[must_use] pub fn new() -> Self { Self::default() } + #[must_use] pub fn vertices(mut self, vertices_bytes: vertex_buffer::VertexBuffer) -> Self { self.vertex_buf = vertices_bytes; self } + #[must_use] pub fn indices(mut self, indices: impl IntoIterator<Item = u32>) -> Self { self.indices = Some(indices.into_iter().collect()); self } + #[must_use] pub fn build(self) -> Mesh { Mesh { @@ -356,6 +364,7 @@ pub enum VertexAttrType impl VertexAttrType { + #[must_use] pub fn layout(&self) -> Layout { match self { @@ -469,6 +478,7 @@ impl CubeFaceOptions /// would be on the upper left of the face. /// /// The default is `(0, 0)` + #[must_use] pub fn uv_upper_left(mut self, uv_upper_left: Vec2<f32>) -> Self { self.uv_upper_left = uv_upper_left; @@ -479,6 +489,7 @@ impl CubeFaceOptions /// would be on the lower right of the face. /// /// The default is `(1, 1)` + #[must_use] pub fn uv_lower_right(mut self, uv_lower_right: Vec2<f32>) -> Self { self.uv_lower_right = uv_lower_right; diff --git a/engine/src/mesh/vertex_buffer.rs b/engine/src/mesh/vertex_buffer.rs index 5c03b45..64edc58 100644 --- a/engine/src/mesh/vertex_buffer.rs +++ b/engine/src/mesh/vertex_buffer.rs @@ -75,6 +75,7 @@ pub struct VertexBuffer impl VertexBuffer { + #[must_use] pub unsafe fn from_raw( vertex_attr_props: &[VertexAttrProperties], buffer: Vec<u8>, @@ -95,6 +96,7 @@ impl VertexBuffer } } + #[must_use] pub fn with_capacity(vertex_attrs: &[VertexAttrInfo], capacity: usize) -> Self { let mut vertex_attr_props = vertex_attrs @@ -169,11 +171,13 @@ impl VertexBuffer } } + #[must_use] pub fn vertex_attr_props(&self) -> &[VertexAttrProperties] { &self.vertex_attr_props } + #[must_use] pub fn len(&self) -> usize { assert_eq!(self.buf.len() % self.vertex_size, 0, "Invalid length"); @@ -181,11 +185,19 @@ impl VertexBuffer self.buf.len() / self.vertex_size } + #[must_use] + pub fn is_empty(&self) -> bool + { + self.buf.is_empty() + } + + #[must_use] pub fn vertex_size(&self) -> usize { self.vertex_size } + #[must_use] pub fn as_bytes(&self) -> &[u8] { &self.buf @@ -196,6 +208,7 @@ impl VertexBuffer self.buf.clear(); } + #[must_use] pub fn iter<VertexAttr: VertexAttrValue>( &self, vertex_label: VertexLabel, diff --git a/engine/src/model.rs b/engine/src/model.rs index 1269afd..560c43d 100644 --- a/engine/src/model.rs +++ b/engine/src/model.rs @@ -14,6 +14,7 @@ pub struct Model impl Model { + #[must_use] pub fn new(asset_handle: AssetHandle<Spec>) -> Self { Self { spec_asset: asset_handle } @@ -30,6 +31,7 @@ pub struct Spec impl Spec { + #[must_use] pub fn builder() -> SpecBuilder { SpecBuilder::default() @@ -62,6 +64,7 @@ pub struct SpecBuilder impl SpecBuilder { + #[must_use] pub fn mesh(mut self, asset: AssetHandle<Mesh>) -> Self { self.mesh_asset = Some(asset); @@ -69,6 +72,7 @@ impl SpecBuilder self } + #[must_use] pub fn materials( mut self, materials: impl IntoIterator<Item = MaterialDescription>, @@ -98,6 +102,7 @@ pub struct MaterialDescription impl MaterialDescription { + #[must_use] pub fn new(asset: AssetHandle<Material>) -> Self { Self { asset } diff --git a/engine/src/model/asset.rs b/engine/src/model/asset.rs index 52a6733..ff5eddf 100644 --- a/engine/src/model/asset.rs +++ b/engine/src/model/asset.rs @@ -24,6 +24,7 @@ impl Settings /// their origin at the bottom left corner of the image /// /// The default is `true`. + #[must_use] pub fn y_flip_uvs(mut self, y_flip_uvs: bool) -> Self { self.y_flip_uvs = y_flip_uvs; diff --git a/engine/src/projection.rs b/engine/src/projection.rs index b3bfa00..91a0259 100644 --- a/engine/src/projection.rs +++ b/engine/src/projection.rs @@ -15,6 +15,7 @@ pub enum Projection impl Projection { + #[must_use] pub fn to_matrix_rh( &self, window_size: PhysicalSize<u32>, @@ -47,6 +48,7 @@ impl Perspective { /// Creates a perspective projection matrix using right-handed coordinates. #[inline] + #[must_use] pub fn to_matrix_rh(&self, aspect: f32, clip_volume: ClipVolume) -> Matrix<f32, 4, 4> { @@ -107,12 +109,14 @@ pub struct Orthographic impl Orthographic { + #[must_use] pub fn builder() -> OrthographicBuilder { OrthographicBuilder::default() } /// Creates a orthographic projection matrix using right-handed coordinates. + #[must_use] pub fn to_matrix_rh( &self, window_size: PhysicalSize<u32>, @@ -187,6 +191,7 @@ pub struct OrthographicParams } /// Creates a orthographic projection matrix using right-handed coordinates. +#[must_use] pub fn orthographic_rh( OrthographicParams { left, right, bottom, top, near, far }: OrthographicParams, clip_volume: ClipVolume, diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs index 6840dbe..bf0ab4e 100644 --- a/engine/src/rendering.rs +++ b/engine/src/rendering.rs @@ -57,6 +57,7 @@ pub struct Extension { impl Extension { + #[must_use] pub fn builder() -> ExtensionBuilder { ExtensionBuilder::default() @@ -131,6 +132,7 @@ pub struct GraphicsProperties impl GraphicsProperties { + #[must_use] pub fn builder() -> GraphicsPropertiesBuilder { GraphicsPropertiesBuilder::default() @@ -147,12 +149,14 @@ impl Default for GraphicsProperties impl GraphicsPropertiesBuilder { + #[must_use] pub fn multisampling_sample_cnt(mut self, multisampling_sample_cnt: u8) -> Self { self.multisampling_sample_cnt = Some(multisampling_sample_cnt); self } + #[must_use] pub fn no_multisampling(mut self) -> Self { self.multisampling_sample_cnt = None; @@ -282,6 +286,7 @@ builder! { impl DrawMeshOptions { + #[must_use] pub fn builder() -> DrawMeshOptionsBuilder { DrawMeshOptionsBuilder::default() @@ -290,6 +295,7 @@ impl DrawMeshOptions impl DrawMeshOptionsBuilder { + #[must_use] pub fn element_cnt(mut self, element_cnt: u32) -> Self { self.element_cnt = Some(element_cnt); @@ -535,7 +541,7 @@ fn enqueue_commands_from_render_passes( window.inner_size, )); - window_surface.size = window.inner_size.clone(); + window_surface.size = window.inner_size; } } @@ -556,7 +562,7 @@ fn enqueue_commands_from_render_passes( Command::UpdateDrawProperties(draw_props, _) => Some(draw_props.clone()), _ => None, }) - .last(); + .next_back(); command_queue.queue.extend(render_pass.commands); @@ -664,6 +670,7 @@ pub enum TexturePixelDataFormat impl TexturePixelDataFormat { + #[must_use] pub fn for_image(image: &Image) -> Option<Self> { let is_srgb = image.color_space_is_srgb(); diff --git a/engine/src/rendering/backend.rs b/engine/src/rendering/backend.rs index d69b338..495f223 100644 --- a/engine/src/rendering/backend.rs +++ b/engine/src/rendering/backend.rs @@ -3,6 +3,7 @@ use ecs::extension::Extension; pub mod opengl; /// Returns the default rendering backend. +#[must_use] pub fn get_default() -> impl Extension { self::opengl::Extension::default() diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs index 8b887b3..0a04d6e 100644 --- a/engine/src/rendering/backend/opengl.rs +++ b/engine/src/rendering/backend/opengl.rs @@ -419,7 +419,7 @@ impl BackendShaderBinding ); } BackendShaderBinding::Texture(texture) => { - texture.bind_to_texture_unit(gl_context, binding_index.0) + texture.bind_to_texture_unit(gl_context, binding_index.0); } } } @@ -542,10 +542,9 @@ fn prepare_windows( let window_handle = match window .as_ref() - .map(|window| unsafe { + .and_then(|window| unsafe { windowing_context.get_window_as_handle(&window.wid()) }) - .flatten() .transpose() { Ok(window_handle) => window_handle, @@ -555,7 +554,7 @@ fn prepare_windows( } }; - let (new_window_creation_attrs, gl_config) = match DisplayBuilder::new() + let (new_window_creation_attrs, gl_config) = match DisplayBuilder::default() .with_window_attributes(window_creation_attrs.clone()) .build( window_handle, @@ -638,7 +637,7 @@ fn init_window_graphics( }; let Ok(window_inner_size) = - PhysicalSize::<NonZero<u32>>::try_convert_from(window.inner_size.clone()) + PhysicalSize::<NonZero<u32>>::try_convert_from(window.inner_size) else { tracing::error!( "Cannot create a surface for a window with a width/height of 0", @@ -669,7 +668,7 @@ fn init_window_graphics( let gl_context = match graphics_ctx.gl_context.get_or_try_insert_with_fn(|| { create_gl_context( &window_gl_config.gl_config, - &graphics_props, + graphics_props, window_handle, &window_surface, ) @@ -684,10 +683,10 @@ fn init_window_graphics( if let Err(err) = gl_context.make_current(&window_surface) { tracing::error!("Failed to make GL context current: {err}"); continue; - }; + } if let Err(err) = gl_set_viewport( - &gl_context, + gl_context, &Vec2 { x: 0, y: 0 }.into(), &opengl_bindings::data_types::Dimens { width: window.inner_size.width, @@ -697,26 +696,22 @@ fn init_window_graphics( tracing::error!("Failed to set viewport: {err}"); } - set_enabled( - &gl_context, - Capability::DepthTest, - graphics_props.depth_test, - ); + set_enabled(gl_context, Capability::DepthTest, graphics_props.depth_test); set_enabled( - &gl_context, + gl_context, Capability::MultiSample, graphics_props.multisampling_sample_cnt.is_some(), ); if graphics_props.debug { - enable(&gl_context, Capability::DebugOutput); - enable(&gl_context, Capability::DebugOutputSynchronous); + enable(gl_context, Capability::DebugOutput); + enable(gl_context, Capability::DebugOutputSynchronous); - set_debug_message_callback(&gl_context, opengl_debug_message_cb); + set_debug_message_callback(gl_context, opengl_debug_message_cb); match set_debug_message_control( - &gl_context, + gl_context, None, None, None, @@ -739,7 +734,7 @@ fn init_window_graphics( window_ent_id, (Surface { id: surface_id, - size: window.inner_size.clone(), + size: window.inner_size, },), ); @@ -747,7 +742,7 @@ fn init_window_graphics( surface_id, GraphicsContextSurface { window_surface, - size: window.inner_size.clone(), + size: window.inner_size, }, ); } @@ -927,7 +922,7 @@ fn handle_commands( program.activate(gl_context); - for (binding_index, binding) in bindings.iter() { + for (binding_index, binding) in bindings { binding.bind(gl_context, binding_index); } @@ -945,7 +940,7 @@ fn handle_commands( backend_resources, object_store, gl_context, - shader_object_id.clone(), + shader_object_id, &binding_location, binding_value, ); @@ -1097,7 +1092,7 @@ fn handle_commands( Command::CreateMesh { obj_id, mesh, usage: mesh_usage } => { let mesh = match &mesh { AssetOrValue::Asset(mesh_asset) => { - let Some(mesh) = assets.get(&mesh_asset) else { + let Some(mesh) = assets.get(mesh_asset) else { tracing::error!( asset_id=?mesh_asset.id(), "Mesh asset does not exist" @@ -1116,7 +1111,7 @@ fn handle_commands( obj_id, || { Ok(BackendResource::Mesh { - mesh: GraphicsMesh::new(gl_context, &mesh, mesh_usage)?, + mesh: GraphicsMesh::new(gl_context, mesh, mesh_usage)?, vertex_attrs_updated_for_shader: None, }) }, @@ -1201,7 +1196,7 @@ fn handle_commands( if let Err(err) = draw_mesh(gl_context, graphics_mesh, &draw_mesh_opts) { tracing::error!("Failed to draw mesh: {err}"); - }; + } } Command::UpdateDrawProperties(properties, update_flags) => { update_draw_properties( @@ -1237,7 +1232,7 @@ fn create_gl_context( } .map_err(CreateGlContextError::CreateGlutinContext)?; - MaybeCurrentContextWithFns::new(glutin_context, &surface) + MaybeCurrentContextWithFns::new(glutin_context, surface) .map_err(CreateGlContextError::MakeContextCurrent) } @@ -1519,8 +1514,8 @@ fn update_framebuffer_properties( } } -fn set_shader_binding<'backend_resources>( - backend_resources: &'backend_resources mut BackendResourceStore, +fn set_shader_binding( + backend_resources: &mut BackendResourceStore, object_store: &ObjectStore, gl_context: &MaybeCurrentContextWithFns, shader_object_id: ObjectId, @@ -1549,14 +1544,11 @@ fn set_shader_binding<'backend_resources>( None => return None, }; - if let Some(prev_binding) = + // TODO: Textures should probably also be handled somehow here + if let Some(BackendShaderBinding::Uniform(prev_binding_uniform_buf)) = shader_bindings.remove(ShaderBindingIndex(binding_location.binding_index)) { - // TODO: Textures should probably also be handled somehow here - if let BackendShaderBinding::Uniform(prev_binding_uniform_buf) = prev_binding - { - prev_binding_uniform_buf.delete(gl_context); - } + prev_binding_uniform_buf.delete(gl_context); } return Some( @@ -1588,27 +1580,27 @@ fn set_shader_binding<'backend_resources>( None => return None, }; - let binding = match shader_bindings - .get(ShaderBindingIndex(binding_location.binding_index)) - .cloned() + let binding = if let Some(binding @ BackendShaderBinding::Uniform(_)) = + shader_bindings + .get(ShaderBindingIndex(binding_location.binding_index)) + .cloned() { - Some(binding @ BackendShaderBinding::Uniform(_)) => binding, - Some(_) | None => { - let uniform_buf = GlBuffer::<u8>::new(gl_context); + binding + } else { + let uniform_buf = GlBuffer::<u8>::new(gl_context); - uniform_buf - .init( - gl_context, - binding_location.binding_size, - opengl_bindings::buffer::Usage::Dynamic, - ) - .unwrap(); + uniform_buf + .init( + gl_context, + binding_location.binding_size, + opengl_bindings::buffer::Usage::Dynamic, + ) + .unwrap(); - shader_bindings - .entry(ShaderBindingIndex(binding_location.binding_index)) - .set_or_insert_with(|| BackendShaderBinding::Uniform(uniform_buf)) - .clone() - } + shader_bindings + .entry(ShaderBindingIndex(binding_location.binding_index)) + .set_or_insert_with(|| BackendShaderBinding::Uniform(uniform_buf)) + .clone() }; let BackendShaderBinding::Uniform(binding_uniform_buf) = &binding else { @@ -1828,8 +1820,7 @@ fn create_shader_program( stage: ShaderStage::Vertex, entrypoint: vs_entry_point_reflection .name() - .map(|name| name.to_string().into()) - .unwrap_or("(none)".into()), + .map_or("(none)".into(), |name| name.to_string().into()), })?; let (fs_entry_point_index, fs_entry_point_reflection) = shader_program_reflection @@ -1849,15 +1840,14 @@ fn create_shader_program( stage: ShaderStage::Fragment, entrypoint: fs_entry_point_reflection .name() - .map(|name| name.to_string().into()) - .unwrap_or("(none)".into()), + .map_or("(none)".into(), |name| name.to_string().into()), })?; let vertex_shader = GlShader::new(current_context, ShaderKind::Vertex); vertex_shader.set_source( current_context, - &vertex_shader_entry_point_code.as_str().unwrap(), + vertex_shader_entry_point_code.as_str().unwrap(), )?; vertex_shader.compile(current_context)?; @@ -1866,7 +1856,7 @@ fn create_shader_program( fragment_shader.set_source( current_context, - &fragment_shader_entry_point_code.as_str().unwrap(), + fragment_shader_entry_point_code.as_str().unwrap(), )?; fragment_shader.compile(current_context)?; @@ -1938,7 +1928,7 @@ fn opengl_debug_message_cb( _ => { emit_log_with_message!(tracing::Level::WARN); } - }; + } } #[inline] diff --git a/engine/src/rendering/backend/opengl/glutin_compat.rs b/engine/src/rendering/backend/opengl/glutin_compat.rs index 27f82ad..0bdfcd2 100644 --- a/engine/src/rendering/backend/opengl/glutin_compat.rs +++ b/engine/src/rendering/backend/opengl/glutin_compat.rs @@ -66,12 +66,6 @@ pub struct DisplayBuilder impl DisplayBuilder { - /// Create new display builder. - pub fn new() -> Self - { - Default::default() - } - /// The preference in picking the configuration. #[allow(dead_code)] pub fn with_preference(mut self, preference: ApiPreference) -> Self @@ -153,7 +147,7 @@ impl DisplayBuilder config_picker_fn(gl_configs).ok_or(Error::NoConfigPicked)?; let window_attrs = cfg_select! { - windows => { self.window_attributes } + windows => self.window_attributes, _ => { finalize_window_creation_attrs(self.window_attributes, &picked_gl_config) } @@ -179,39 +173,36 @@ pub enum Error WindowRequired, } +#[allow(unused_variables)] fn create_display( display_handle: &DisplayHandle<'_>, - _api_preference: ApiPreference, - _raw_window_handle: Option<RawWindowHandle>, + api_preference: ApiPreference, + raw_window_handle: Option<RawWindowHandle>, ) -> Result<Display, GlutinError> { let preference = cfg_select! { - windows => { - match _api_preference { - ApiPreference::PreferEgl => { - DisplayApiPreference::EglThenWgl(_raw_window_handle) - } - ApiPreference::FallbackEgl => { - DisplayApiPreference::WglThenEgl(_raw_window_handle) - } + windows => match _api_preference { + ApiPreference::PreferEgl => { + DisplayApiPreference::EglThenWgl(_raw_window_handle) } - } - target_os = "linux" => { - match _api_preference { - ApiPreference::PreferEgl => DisplayApiPreference::EglThenGlx(Box::new( - crate::windowing::window::platform::x11::register_xlib_error_hook, - )), - ApiPreference::FallbackEgl => DisplayApiPreference::GlxThenEgl(Box::new( - crate::windowing::window::platform::x11::register_xlib_error_hook, - )), + ApiPreference::FallbackEgl => { + DisplayApiPreference::WglThenEgl(_raw_window_handle) } - } - target_os = "macos" => { DisplayApiPreference::Cgl } + }, + target_os = "linux" => match api_preference { + ApiPreference::PreferEgl => DisplayApiPreference::EglThenGlx(Box::new( + crate::windowing::window::platform::x11::register_xlib_error_hook, + )), + ApiPreference::FallbackEgl => DisplayApiPreference::GlxThenEgl(Box::new( + crate::windowing::window::platform::x11::register_xlib_error_hook, + )), + }, + target_os = "macos" => DisplayApiPreference::Cgl, }; let handle = display_handle.as_raw(); - unsafe { Ok(Display::new(handle, preference)?) } + unsafe { Display::new(handle, preference) } } /// Finalize [`Window`] creation by applying the options from the [`Config`], be @@ -235,7 +226,8 @@ fn finalize_window_creation_attrs( if let Some(x11_visual) = glutin::platform::x11::X11GlConfigExt::x11_visual(gl_config) { return attributes.with_x_visual_id(Some( - x11_visual.visual_id() as crate::windowing::window::XVisualID + crate::windowing::window::XVisualID::try_from(x11_visual.visual_id()) + .expect("X visual ID is too large"), )); } diff --git a/engine/src/rendering/backend/opengl/graphics_mesh.rs b/engine/src/rendering/backend/opengl/graphics_mesh.rs index 43cd254..899f50f 100644 --- a/engine/src/rendering/backend/opengl/graphics_mesh.rs +++ b/engine/src/rendering/backend/opengl/graphics_mesh.rs @@ -69,8 +69,7 @@ impl GraphicsMesh max_value, } => { panic!( - "Size of vertex ({}) is too large. Must be less than {max_value}", - value + "Size of vertex ({value}) is too large. Must be less than {max_value}" ); } } @@ -86,7 +85,7 @@ impl GraphicsMesh vertex_arr.bind_element_buffer(current_context, &index_buffer); return Ok(Self { - vertex_buffer: vertex_buffer, + vertex_buffer, vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), last_max_vertex_attr_index: 0, index_buffer: Some(index_buffer), @@ -99,7 +98,7 @@ impl GraphicsMesh } Ok(Self { - vertex_buffer: vertex_buffer, + vertex_buffer, vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), last_max_vertex_attr_index: 0, index_buffer: None, @@ -135,7 +134,7 @@ impl GraphicsMesh .map_err(Error::StoreIndicesFailed)?; self.vertex_arr - .bind_element_buffer(current_context, &index_buffer); + .bind_element_buffer(current_context, index_buffer); self.element_cnt = indices .len() diff --git a/engine/src/rendering/main_render_pass.rs b/engine/src/rendering/main_render_pass.rs index c762231..cb38071 100644 --- a/engine/src/rendering/main_render_pass.rs +++ b/engine/src/rendering/main_render_pass.rs @@ -237,9 +237,9 @@ pub fn add_main_render_pass( continue; } - let model_material = match model_spec.find_first_material(&assets) { + let model_material = match model_spec.find_first_material(assets) { MaterialSearchResult::Found(model_material_asset) - if let Some(model_material) = assets.get(&model_material_asset) => + if let Some(model_material) = assets.get(model_material_asset) => { model_material } @@ -251,7 +251,7 @@ pub fn add_main_render_pass( if model_material .textures() - .any(|texture_asset| !assets.is_loaded_and_has_type(&texture_asset)) + .any(|texture_asset| !assets.is_loaded_and_has_type(texture_asset)) { continue; } @@ -273,13 +273,7 @@ pub fn add_main_render_pass( .commands .push(Command::ActivateShader(shaders.main_3d_shader_obj_id)); - if let Some(draw_flags) = draw_flags.as_deref().and_then(|draw_flags| { - if draw_flags.polygon_mode_config != PolygonModeConfig::default() { - Some(draw_flags) - } else { - None - } - }) { + if let Some(draw_flags) = draw_flags.as_deref().filter(|&draw_flags| draw_flags.polygon_mode_config != PolygonModeConfig::default()) { render_pass.commands.push(Command::UpdateDrawProperties( DrawProperties { polygon_mode_config: draw_flags.polygon_mode_config.clone(), @@ -310,7 +304,7 @@ pub fn add_main_render_pass( if let Some(scene_skybox) = &scene_skybox { let Some(sky_box_ids) = load_sky_box( scene_ent_id, - &scene_skybox, + scene_skybox, scene_skybox_state.as_deref(), assets, object_store, @@ -489,9 +483,9 @@ fn add_renderable_creation_commands( debug_assert!(model_spec.materials.len() <= 1); - let model_material = match model_spec.find_first_material(&assets) { + let model_material = match model_spec.find_first_material(assets) { MaterialSearchResult::Found(model_material_asset) => { - let Some(model_material) = assets.get(&model_material_asset) else { + let Some(model_material) = assets.get(model_material_asset) else { return; }; @@ -726,7 +720,7 @@ fn add_set_3d_shader_renderable_bindings( { let (material, material_flags, transform) = renderable; - let transform = match transform.as_deref() { + let transform = match transform { Some(transform) => transform, None => &Transform::default(), }; @@ -735,11 +729,10 @@ fn add_set_3d_shader_renderable_bindings( let inverted_model_matrix = model_matrix.inverse(); let material_flags = material_flags - .as_deref() .unwrap_or(&const { MaterialFlags::builder().build() }); let env_lighting = match &scene_env_lighting { - Some(env_lighting) => &env_lighting, + Some(env_lighting) => env_lighting, None => &EnvironmentalLighting::default(), }; @@ -754,8 +747,7 @@ fn add_set_3d_shader_renderable_bindings( let diffuse_map_obj_id = material .diffuse_map .as_ref() - .map(|diffuse_map| ObjectId::Asset(diffuse_map.id())) - .unwrap_or(white_1x1_tex_obj_id); + .map_or(white_1x1_tex_obj_id, |diffuse_map| ObjectId::Asset(diffuse_map.id())); render_pass.commands.extend( [ @@ -766,10 +758,8 @@ fn add_set_3d_shader_renderable_bindings( .field("model_inverted") .binding(inverted_model_matrix.into())?, material_shader_cursor.field("ambient").binding( - material_flags - .use_ambient_color - .then_some(&material.ambient) - .unwrap_or(&env_lighting.ambient_color) + if material_flags + .use_ambient_color { &material.ambient } else { &env_lighting.ambient_color } .to_rgb_lossy() .into(), )?, @@ -784,8 +774,7 @@ fn add_set_3d_shader_renderable_bindings( material .ambient_map .as_ref() - .map(|ambient_map| ObjectId::Asset(ambient_map.id())) - .unwrap_or(diffuse_map_obj_id), + .map_or(diffuse_map_obj_id, |ambient_map| ObjectId::Asset(ambient_map.id())), ShaderBindingTextureKind::Texture2D, ), )?, @@ -800,8 +789,7 @@ fn add_set_3d_shader_renderable_bindings( material .specular_map .as_ref() - .map(|specular_map| ObjectId::Asset(specular_map.id())) - .unwrap_or(white_1x1_tex_obj_id), + .map_or(white_1x1_tex_obj_id, |specular_map| ObjectId::Asset(specular_map.id())), ShaderBindingTextureKind::Texture2D, ), )?, @@ -907,24 +895,21 @@ fn load_sky_box( actions: &mut Actions, ) -> Result<Option<SkyBoxIds>, crate::Error> { - let mesh_object_id = match &sky_box_state { - Some(sky_box_state) => sky_box_state.mesh_object_id, - None => { - let sky_box_mesh = - Mesh::cube(Dimens3 { width: 1.0, height: 1.0, depth: 1.0 }); + let mesh_object_id = if let Some(sky_box_state) = &sky_box_state { sky_box_state.mesh_object_id } else { + let sky_box_mesh = + Mesh::cube(Dimens3 { width: 1.0, height: 1.0, depth: 1.0 }); - let mesh_object_id = ObjectId::new_sequential(); + let mesh_object_id = ObjectId::new_sequential(); - object_store.insert_pending(mesh_object_id); + object_store.insert_pending(mesh_object_id); - render_pass.commands.push(Command::CreateMesh { - obj_id: mesh_object_id, - mesh: AssetOrValue::Value(sky_box_mesh), - usage: MeshUsage::Static, - }); + render_pass.commands.push(Command::CreateMesh { + obj_id: mesh_object_id, + mesh: AssetOrValue::Value(sky_box_mesh), + usage: MeshUsage::Static, + }); - mesh_object_id - } + mesh_object_id }; let texture_object_id = match &sky_box { @@ -952,7 +937,7 @@ fn load_sky_box( }) .collect::<Vec<_>>() .as_array::<6>() - .cloned() + .copied() else { return Ok(None); }; diff --git a/engine/src/rendering/object.rs b/engine/src/rendering/object.rs index 61e7ed5..d496eda 100644 --- a/engine/src/rendering/object.rs +++ b/engine/src/rendering/object.rs @@ -26,6 +26,7 @@ impl Id )) } + #[must_use] pub fn into_asset_id(self) -> Option<AssetId> { match self { @@ -55,16 +56,19 @@ pub struct Store impl Store { + #[must_use] pub fn get_obj(&self, id: &Id) -> Option<&Object> { self.objects.get(id).and_then(|obj| obj.as_ref()) } + #[must_use] pub fn contains_maybe_pending_with_id(&self, id: &Id) -> bool { self.objects.contains_key(id) } + #[must_use] pub fn contains_non_pending_with_id(&self, id: &Id) -> bool { self.objects.get(id).and_then(|obj| obj.as_ref()).is_some() @@ -96,16 +100,19 @@ pub struct Object impl Object { + #[must_use] pub fn from_raw(raw: RawValue, kind: Kind) -> Self { Self { raw, kind } } + #[must_use] pub fn as_raw(&self) -> RawValue { self.raw } + #[must_use] pub fn kind(&self) -> Kind { self.kind diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index 3f07381..085773b 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -70,6 +70,7 @@ pub struct Module impl Module { + #[must_use] pub fn entry_points(&self) -> impl ExactSizeIterator<Item = EntryPoint> + use<'_> { self.inner @@ -77,6 +78,7 @@ impl Module .map(|entry_point| EntryPoint { inner: entry_point }) } + #[must_use] pub fn get_entry_point(&self, entry_point: &str) -> Option<EntryPoint> { let entry_point = self.inner.find_entry_point_by_name(entry_point)?; @@ -84,6 +86,7 @@ impl Module Some(EntryPoint { inner: entry_point }) } + #[must_use] pub fn file_path(&self) -> &str { self.inner.file_path() @@ -97,6 +100,7 @@ pub struct EntryPoint impl EntryPoint { + #[must_use] pub fn function(&self) -> FunctionReflection<'_> { FunctionReflection { @@ -110,8 +114,9 @@ pub struct FunctionReflection<'a> inner: &'a shader_slang::reflection::Function, } -impl<'a> FunctionReflection<'a> +impl FunctionReflection<'_> { + #[must_use] pub fn name(&self) -> Option<&str> { self.inner.name() @@ -125,21 +130,25 @@ pub struct EntryPointReflection<'a> impl<'a> EntryPointReflection<'a> { + #[must_use] pub fn name(&self) -> Option<&str> { self.inner.name() } + #[must_use] pub fn name_override(&self) -> Option<&str> { self.inner.name_override() } + #[must_use] pub fn stage(&self) -> Stage { Stage::from_slang_stage(self.inner.stage()) } + #[must_use] pub fn parameters(&self) -> impl ExactSizeIterator<Item = VariableLayout<'a>> { self.inner @@ -147,6 +156,7 @@ impl<'a> EntryPointReflection<'a> .map(|param| VariableLayout { inner: param }) } + #[must_use] pub fn var_layout(&self) -> Option<VariableLayout<'a>> { Some(VariableLayout { inner: self.inner.var_layout()? }) @@ -172,6 +182,7 @@ impl Program }) } + #[must_use] pub fn metadata(&self) -> &ProgramMetadata { &self.metadata @@ -186,7 +197,7 @@ impl Program pub fn reflection(&self, target: u32) -> Result<ProgramReflection<'_>, Error> { - let reflection = self.inner.layout(target as i64)?; + let reflection = self.inner.layout(i64::from(target))?; Ok(ProgramReflection { inner: reflection }) } @@ -209,6 +220,7 @@ pub struct ProgramReflection<'a> impl<'a> ProgramReflection<'a> { + #[must_use] pub fn get_entry_point_by_index(&self, index: u32) -> Option<EntryPointReflection<'a>> { @@ -217,6 +229,7 @@ impl<'a> ProgramReflection<'a> }) } + #[must_use] pub fn get_entry_point_by_name(&self, name: &str) -> Option<EntryPointReflection<'a>> { @@ -225,6 +238,7 @@ impl<'a> ProgramReflection<'a> }) } + #[must_use] pub fn entry_points( &self, ) -> impl ExactSizeIterator<Item = EntryPointReflection<'a>> + use<'a> @@ -234,6 +248,7 @@ impl<'a> ProgramReflection<'a> .map(|entry_point| EntryPointReflection { inner: entry_point }) } + #[must_use] pub fn global_params_type_layout(&self) -> Option<TypeLayout<'a>> { Some(TypeLayout { @@ -241,6 +256,7 @@ impl<'a> ProgramReflection<'a> }) } + #[must_use] pub fn global_params_var_layout(&self) -> Option<VariableLayout<'a>> { Some(VariableLayout { @@ -248,6 +264,7 @@ impl<'a> ProgramReflection<'a> }) } + #[must_use] pub fn get_type(&self, name: &str) -> Option<TypeReflection<'a>> { Some(TypeReflection { @@ -255,12 +272,13 @@ impl<'a> ProgramReflection<'a> }) } + #[must_use] pub fn get_type_layout(&self, ty: &TypeReflection<'a>) -> Option<TypeLayout<'a>> { Some(TypeLayout { inner: self .inner - .type_layout(&ty.inner, shader_slang::LayoutRules::Default)?, + .type_layout(ty.inner, shader_slang::LayoutRules::Default)?, }) } } @@ -273,16 +291,19 @@ pub struct VariableLayout<'a> impl<'a> VariableLayout<'a> { + #[must_use] pub fn name(&self) -> Option<&'a str> { self.inner.name() } + #[must_use] pub fn semantic_name(&self) -> Option<&str> { self.inner.semantic_name() } + #[must_use] pub fn binding_index(&self) -> u32 { self.inner @@ -291,6 +312,7 @@ impl<'a> VariableLayout<'a> // self.inner.binding_index() } + #[must_use] pub fn varying_input_offset(&self) -> Option<usize> { if !self @@ -304,26 +326,31 @@ impl<'a> VariableLayout<'a> Some(self.inner.offset(SlangParameterCategory::VaryingInput)) } + #[must_use] pub fn binding_space(&self) -> u32 { self.inner.binding_space() } + #[must_use] pub fn semantic_index(&self) -> usize { self.inner.semantic_index() } + #[must_use] pub fn offset(&self) -> usize { self.inner.offset(shader_slang::ParameterCategory::Uniform) } + #[must_use] pub fn ty(&self) -> Option<TypeReflection<'a>> { self.inner.ty().map(|ty| TypeReflection { inner: ty }) } + #[must_use] pub fn type_layout(&self) -> Option<TypeLayout<'a>> { Some(TypeLayout { inner: self.inner.type_layout()? }) @@ -338,11 +365,13 @@ pub struct TypeLayout<'a> impl<'a> TypeLayout<'a> { + #[must_use] pub fn kind(&self) -> TypeKind { TypeKind::from_slang_type_kind(self.inner.kind()) } + #[must_use] pub fn scalar_type(&self) -> Option<ScalarType> { Some(ScalarType::from_slang_scalar_type( @@ -350,6 +379,7 @@ impl<'a> TypeLayout<'a> )) } + #[must_use] pub fn resource_shape(&self) -> Option<ResourceShape> { Some(ResourceShape::from_bits_retain( @@ -357,6 +387,7 @@ impl<'a> TypeLayout<'a> )) } + #[must_use] pub fn get_field_by_name(&self, name: &str) -> Option<VariableLayout<'a>> { let index = self.inner.find_field_index_by_name(name); @@ -372,16 +403,19 @@ impl<'a> TypeLayout<'a> Some(VariableLayout { inner: field }) } + #[must_use] pub fn parameter_category(&self) -> ParameterCategory { ParameterCategory::from_slang_parameter_category(self.inner.parameter_category()) } + #[must_use] pub fn binding_range_descriptor_set_index(&self, index: i64) -> i64 { self.inner.binding_range_descriptor_set_index(index) } + #[must_use] pub fn get_field_binding_range_offset_by_name(&self, name: &str) -> Option<u64> { let field_index = self.inner.find_field_index_by_name(name); @@ -400,40 +434,47 @@ impl<'a> TypeLayout<'a> Some(field_binding_range_offset.cast_unsigned()) } + #[must_use] pub fn ty(&self) -> Option<TypeReflection<'a>> { self.inner.ty().map(|ty| TypeReflection { inner: ty }) } + #[must_use] pub fn fields(&self) -> FieldIter<'a> { FieldIter { - type_layout: self.clone(), + type_layout: *self, cnt: self.field_cnt(), index: 0, } } + #[must_use] pub fn field_cnt(&self) -> u32 { self.inner.field_count() } + #[must_use] pub fn element_cnt(&self) -> Option<usize> { self.inner.element_count() } + #[must_use] pub fn row_cnt(&self) -> Option<u32> { self.inner.row_count() } + #[must_use] pub fn column_cnt(&self) -> Option<u32> { self.inner.column_count() } + #[must_use] pub fn element_type_layout(&self) -> Option<TypeLayout<'a>> { self.inner @@ -441,6 +482,7 @@ impl<'a> TypeLayout<'a> .map(|type_layout| TypeLayout { inner: type_layout }) } + #[must_use] pub fn element_var_layout(&self) -> Option<VariableLayout<'a>> { self.inner @@ -448,6 +490,7 @@ impl<'a> TypeLayout<'a> .map(|var_layout| VariableLayout { inner: var_layout }) } + #[must_use] pub fn container_var_layout(&self) -> Option<VariableLayout<'a>> { self.inner @@ -455,6 +498,7 @@ impl<'a> TypeLayout<'a> .map(|var_layout| VariableLayout { inner: var_layout }) } + #[must_use] pub fn uniform_size(&self) -> Option<usize> { // tracing::debug!( @@ -483,6 +527,7 @@ impl<'a> TypeLayout<'a> Some(self.inner.size(SlangParameterCategory::Uniform)) } + #[must_use] pub fn stride(&self) -> usize { self.inner.stride(self.inner.categories().next().unwrap()) @@ -556,6 +601,7 @@ pub struct TypeReflection<'a> impl TypeReflection<'_> { + #[must_use] pub fn kind(&self) -> TypeKind { TypeKind::from_slang_type_kind(self.inner.kind()) @@ -787,6 +833,7 @@ pub struct Blob impl Blob { + #[must_use] pub fn as_bytes(&self) -> &[u8] { self.inner.as_slice() @@ -868,11 +915,13 @@ pub struct Context impl Context { + #[must_use] pub fn get_module(&self, asset_id: &AssetId) -> Option<&Module> { self.modules.get(asset_id) } + #[must_use] pub fn get_program(&self, asset_id: &AssetId) -> Option<&Program> { self.programs.get(asset_id) @@ -1008,19 +1057,13 @@ impl VertexDescription ); } - let scalar_type = match ( + let (TypeKind::Scalar | TypeKind::Vector, Some(scalar_type)) = ( var_input.type_layout.kind(), var_input.type_layout.scalar_type(), - ) { - (TypeKind::Scalar, Some(scalar_type)) => scalar_type, - (TypeKind::Vector, Some(scalar_type)) => scalar_type, - _ => { - return Err( - VertexDescriptionError::UnsupportedVertexInputType { - name: name.to_owned(), - }, - ); - } + ) else { + return Err(VertexDescriptionError::UnsupportedVertexInputType { + name: name.to_owned(), + }); }; seen_inputs.insert(semantic_name.clone()); @@ -1072,6 +1115,7 @@ impl VertexInputSemName } } + #[must_use] pub fn matches_vertex_label(&self, vertex_label: &VertexLabel) -> bool { match (self, vertex_label) { @@ -1291,7 +1335,7 @@ pub(super) fn prepare(collector: &mut crate::ecs::extension::Collector<'_>) let session_desc = shader_slang::SessionDesc::default() .targets(&targets) - .search_paths(&[""]) + .search_paths([""]) .options(&session_options); let Some(session) = global_session.create_session(&session_desc) else { @@ -1359,7 +1403,7 @@ fn load_modules( context.modules.insert(*asset_id, module.clone()); if !module_source.link_entrypoints.is_empty() { - assert!(context.programs.get(asset_id).is_none()); + assert!(!context.programs.contains_key(asset_id)); let shader_program = match context .compose_into_program(module, module_source.link_entrypoints) diff --git a/engine/src/rendering/shader/cursor.rs b/engine/src/rendering/shader/cursor.rs index 49f6b47..551830d 100644 --- a/engine/src/rendering/shader/cursor.rs +++ b/engine/src/rendering/shader/cursor.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fmt::Display; use std::hint::cold_path; -use circular_buffer::FixedCircularBuffer; +use circular_buffer::HeapCircularBuffer; use crate::color::{Color, Rgb, Rgba}; use crate::data_types::matrix::Matrix; @@ -27,6 +27,7 @@ pub struct Cursor<'a> impl<'a> Cursor<'a> { + #[must_use] pub fn new(var_layout: VariableLayout<'a>) -> Self { let binding_location = BindingLocation { @@ -38,10 +39,11 @@ impl<'a> Cursor<'a> Self { type_layout: var_layout.type_layout().unwrap(), binding_location, - location_path: LocationPath::default(), + location_path: LocationPath::new(), } } + #[must_use] pub fn field(&self, name: impl Into<Cow<'static, str>>) -> Self { let name = name.into(); @@ -100,6 +102,7 @@ impl<'a> Cursor<'a> } } + #[must_use] pub fn element(mut self, index: usize) -> Self { let element_type_layout = self.type_layout.element_type_layout().unwrap(); @@ -125,15 +128,23 @@ impl<'a> Cursor<'a> } /// Shader cursor location. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct LocationPath { - locations: FixedCircularBuffer<Location, 16>, + locations: HeapCircularBuffer<Location>, is_truncated: bool, } impl LocationPath { + fn new() -> Self + { + Self { + locations: HeapCircularBuffer::with_capacity(16), + is_truncated: false, + } + } + fn push(&mut self, location: Location) { if self.locations.push_back(location).is_some() { @@ -231,12 +242,7 @@ impl BindingValue Self::Float(_) => { ty_kind == TypeKind::Scalar && scalar_ty == Some(ScalarType::Float32) } - Self::FVec3(_) => { - ty_kind == TypeKind::Vector - && element_scalar_ty == Some(ScalarType::Float32) - && element_cnt == Some(3) - } - Self::Color(Color::Rgb(_)) => { + Self::FVec3(_) | Self::Color(Color::Rgb(_)) => { ty_kind == TypeKind::Vector && element_scalar_ty == Some(ScalarType::Float32) && element_cnt == Some(3) diff --git a/engine/src/texture.rs b/engine/src/texture.rs index 8361f12..844d9eb 100644 --- a/engine/src/texture.rs +++ b/engine/src/texture.rs @@ -22,7 +22,7 @@ pub struct Tex2D #[derive(Debug, Clone)] pub struct TexCubeMap { - pub images: [(CubeMapFace, Image); 6], + pub images: Box<[(CubeMapFace, Image); 6]>, pub properties: Properties, } @@ -53,6 +53,7 @@ pub struct Properties impl Properties { + #[must_use] pub fn builder() -> PropertiesBuilder { PropertiesBuilder::default() @@ -118,6 +119,7 @@ pub struct ImportSettings { impl ImportSettings { + #[must_use] pub fn builder() -> ImportSettingsBuilder { ImportSettingsBuilder::default() @@ -145,9 +147,7 @@ fn import( { asset_submitter.submit_store(Texture::Texture2D(Tex2D { image: Image::open(path)?, - properties: settings - .map(|settings| settings.properties.clone()) - .unwrap_or_default(), + properties: settings.map_or_default(|settings| settings.properties.clone()), })); Ok(()) diff --git a/engine/src/transform.rs b/engine/src/transform.rs index 8e768a6..67fbe23 100644 --- a/engine/src/transform.rs +++ b/engine/src/transform.rs @@ -14,18 +14,21 @@ pub struct Transform impl Transform { + #[must_use] pub fn position(mut self, position: Vec3<f32>) -> Self { self.position = position; self } + #[must_use] pub fn scale(mut self, scale: Dimens3<f32>) -> Self { self.scale = scale; self } + #[must_use] pub fn to_matrix(&self) -> Matrix<f32, 4, 4> { let mut matrix = Matrix::new_identity(); diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs index 0a50ff7..c5f7ce2 100644 --- a/engine/src/ui/dear_imgui.rs +++ b/engine/src/ui/dear_imgui.rs @@ -130,12 +130,14 @@ pub struct Extension impl Extension { + #[must_use] pub fn with_start_enabled(mut self, start_enabled: bool) -> Self { self.start_enabled = start_enabled; self } + #[must_use] pub fn with_settings_ini_file( mut self, settings_ini_file_path: Option<PathBuf>, @@ -164,10 +166,12 @@ impl ecs::extension::Extension for Extension tracing::error!("Failed to set path of imgui settings ini file: {err}"); } - assert!( - dear_imgui_rs::HAS_FREETYPE, - "Freetype font rasterizer support is not enabled" - ); + const { + assert!( + dear_imgui_rs::HAS_FREETYPE, + "Freetype font rasterizer support is not enabled" + ); + } unsafe { context.ctx.font_atlas().add_font_from_memory_ttf( @@ -223,8 +227,8 @@ fn handle_window_changed( .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]); let window_size_logical = PhysicalSize { - width: window.inner_size.width as f64, - height: window.inner_size.height as f64, + width: f64::from(window.inner_size.width), + height: f64::from(window.inner_size.height), } .to_logical::<f64>(hidpi_factor); @@ -272,7 +276,7 @@ fn update( window_surface.id, assets, render_passes, - &shader_context, + shader_context, ) else { *state = State::NotInitialized; return Ok(()); @@ -341,11 +345,9 @@ fn initialize_context( shader_context: &ShaderContext, ) -> Option<(AssetHandle<ShaderModuleSource>, RenderingObjectId, Mesh)> { - let shader_asset = if let Some(shader_asset) = + let Some(shader_asset) = assets.get_handle_to_loaded::<ShaderModuleSource>(SHADER_ASSET_LABEL.clone()) - { - shader_asset - } else { + else { assets.store_with_label( SHADER_ASSET_LABEL.clone(), ShaderModuleSource { @@ -362,19 +364,21 @@ fn initialize_context( return None; }; - let hidpi_factor = window.scale_factor().round(); + #[allow(clippy::cast_possible_truncation)] + let hidpi_factor = window.scale_factor().round() as f32; context .ctx .get_io_mut() - .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]); + .set_display_framebuffer_scale([hidpi_factor, hidpi_factor]); let window_size = Dimens { width: window.inner_size.width as f32, height: window.inner_size.height as f32, }; - let window_logical_size = window_size / (hidpi_factor as f32); + #[allow(clippy::cast_possible_truncation)] + let window_logical_size = window_size / hidpi_factor; context .ctx @@ -455,6 +459,7 @@ fn update_inputs( let mouse_pos = mouse.position.to_logical::<f64>(window.scale_factor()); + #[allow(clippy::cast_possible_truncation)] io.add_mouse_pos_event([mouse_pos.x as f32, mouse_pos.y as f32]); if !mouse.curr_tick_scroll_delta.is_zero() { @@ -610,8 +615,8 @@ fn add_drawing_render_pass( height: rect.rect.h.into(), }, offset: Vec2 { - x: rect.rect.x as u32, - y: rect.rect.y as u32, + x: u32::from(rect.rect.x), + y: u32::from(rect.rect.y), }, }, }); @@ -715,7 +720,7 @@ fn add_drawing_render_pass( }, MeshNamedVertexAttr { label: VertexLabel::Color, - value: vertex.rgba().map(|elem| (elem as f32) / 255.0), + value: vertex.rgba().map(|elem| f32::from(elem) / 255.0), }, MeshNamedVertexAttr { label: VertexLabel::UvFromTopLeft, @@ -733,51 +738,47 @@ fn add_drawing_render_pass( }); for command in draw_list.commands() { - match command { - ImguiDrawCmd::Elements { count, cmd_params } => { - let Some(scissor_box) = - calc_draw_cmd_scissor_box(draw_data, &cmd_params) - else { - continue; - }; + if let ImguiDrawCmd::Elements { count, cmd_params } = command { + let Some(scissor_box) = calc_draw_cmd_scissor_box(draw_data, &cmd_params) + else { + continue; + }; - let tex_id = cmd_params.texture_id; + let tex_id = cmd_params.texture_id; - let Some(texture_object_id) = - texture_lookup.get(TextureLookupId::from(tex_id)) - else { - tracing::error!( - "Unknown texture {}. Skipping skipping draw command", - tex_id.id() - ); - continue; - }; + let Some(texture_object_id) = + texture_lookup.get(TextureLookupId::from(tex_id)) + else { + tracing::error!( + "Unknown texture {}. Skipping skipping draw command", + tex_id.id() + ); + continue; + }; - render_pass.commands.extend([ - RenderingCommand::UpdateDrawProperties( - DrawProperties { scissor_box, ..Default::default() }, - DrawPropertiesUpdateFlags::SCISSOR_BOX, - ), - RenderingCommand::SetShaderBinding( - shader_object_id, - shader_cursor.field("main_texture").binding( - ShaderBindingValue::Texture( - *texture_object_id, - ShaderBindingTextureKind::Texture2D, - ), - )?, - ), - RenderingCommand::DrawMesh( - mesh_obj_id, - DrawMeshOptions::builder() - .element_offset(cmd_params.idx_offset.try_into().unwrap()) - .vertex_offset(cmd_params.vtx_offset.try_into().unwrap()) - .element_cnt(count.try_into().unwrap()) - .build(), - ), - ]); - } - _ => {} + render_pass.commands.extend([ + RenderingCommand::UpdateDrawProperties( + DrawProperties { scissor_box, ..Default::default() }, + DrawPropertiesUpdateFlags::SCISSOR_BOX, + ), + RenderingCommand::SetShaderBinding( + shader_object_id, + shader_cursor.field("main_texture").binding( + ShaderBindingValue::Texture( + *texture_object_id, + ShaderBindingTextureKind::Texture2D, + ), + )?, + ), + RenderingCommand::DrawMesh( + mesh_obj_id, + DrawMeshOptions::builder() + .element_offset(cmd_params.idx_offset.try_into().unwrap()) + .vertex_offset(cmd_params.vtx_offset.try_into().unwrap()) + .element_cnt(count.try_into().unwrap()) + .build(), + ), + ]); } } } @@ -973,12 +974,12 @@ mod inner_context_wrapper ctx.io_mut() .set_backend_flags(dear_imgui_rs::BackendFlags::RENDERER_HAS_TEXTURES); - let _renderer_consumer = ctx.create_renderer_consumer().unwrap(); + let renderer_consumer = ctx.create_renderer_consumer().unwrap(); Self { ctx, frame: null_mut(), - _renderer_consumer, + _renderer_consumer: renderer_consumer, } } diff --git a/engine/src/ui/view/transform_3d.rs b/engine/src/ui/view/transform_3d.rs index a426b2a..9c3dbf1 100644 --- a/engine/src/ui/view/transform_3d.rs +++ b/engine/src/ui/view/transform_3d.rs @@ -124,5 +124,5 @@ fn flatten_matrix(mat: [[f32; 4]; 4]) -> [f32; 4 * 4] unreachable!(); }; - flattened.clone() + *flattened } diff --git a/engine/src/ui/view/world.rs b/engine/src/ui/view/world.rs index 6bb9755..95f29d0 100644 --- a/engine/src/ui/view/world.rs +++ b/engine/src/ui/view/world.rs @@ -39,8 +39,8 @@ const BUTTON_RED_ACTIVE: [f32; 4] = [162.0, 68.0, 51.0, 255.0]; pub struct State { spawning_entity: Option<Uid>, - popup_state: Option<PopupState>, - textures: Option<Textures>, + popup: Option<PopupState>, + icon_textures: Option<IconTextures>, } pub fn show( @@ -55,11 +55,12 @@ pub fn show( let State { spawning_entity, - popup_state, - textures, + popup: popup_state, + icon_textures, } = &mut *state; - let textures = textures.get_or_insert_with(|| Textures::new(dear_imgui_context)); + let icon_textures = + icon_textures.get_or_insert_with(|| IconTextures::new(dear_imgui_context)); let Some(frame) = dear_imgui_context.frame() else { return Ok(()); @@ -84,7 +85,7 @@ pub fn show( create_entity_widgets( frame, popup_state, - textures, + icon_textures, &ent_handle, world, &mut actions, @@ -164,7 +165,7 @@ fn create_spawn_button_widgets( fn create_entity_widgets( frame: &dear_imgui_bindings::Ui, popup_state: &mut Option<PopupState>, - textures: &Textures, + icon_textures: &IconTextures, ent_handle: &EntityHandle, world: &World, actions: &mut Actions, @@ -182,12 +183,12 @@ fn create_entity_widgets( return; } - let ent_title = create_entity_title(&ent_handle); + let ent_title = create_entity_title(ent_handle); let mut is_open = false; frame - .table(&format!("ent_{}_table", ent_handle.uid())) + .table(format!("ent_{}_table", ent_handle.uid())) .headers(false) .sizing_policy(dear_imgui_bindings::TableSizingPolicy::FixedFit) .columns([ @@ -209,7 +210,7 @@ fn create_entity_widgets( create_add_component_button_widget( frame, - &ent_handle, + ent_handle, &ent_title, popup_state, world, @@ -217,13 +218,13 @@ fn create_entity_widgets( frame.table_next_column(); - create_despawn_button_widget(frame, textures, ent_handle, actions); + create_despawn_button_widget(frame, icon_textures, ent_handle, actions); frame.table_next_column(); create_rename_entity_button_widget( frame, - textures, + icon_textures, ent_handle, &ent_title, popup_state, @@ -235,7 +236,7 @@ fn create_entity_widgets( if component_id.is_pair() { create_pair_component_widgets( frame, - textures, + icon_textures, ent_handle, component_id, world, @@ -320,15 +321,15 @@ fn create_component_widgets( ItemRef::Mutable(&mut *component), &mut component_changed, ItemInfo { - item_title: component_info.name, - item_tag: &format!( + title: component_info.name, + tag: &format!( "world_view_entity_{}_component_{}", ent_handle.uid(), component_info.name, ), - item_type_name: None, - item_type, - item_is_read_only: false, + type_name: None, + ty: item_type, + is_read_only: false, }, &[], ); @@ -342,7 +343,7 @@ fn create_component_widgets( fn create_pair_component_widgets( frame: &dear_imgui_bindings::Ui, - textures: &Textures, + icon_textures: &IconTextures, ent_handle: &EntityHandle, pair_id: Uid, world: &World, @@ -418,7 +419,7 @@ fn create_pair_component_widgets( let Some(pair_data_ty) = pair_data_comp_info.type_reflection else { frame.same_line(); - frame.image(textures.warning_icon_tex_id, [16.0, 16.0]); + frame.image(icon_textures.warning, [16.0, 16.0]); if frame.is_item_hovered() { frame.tooltip_text(format!( @@ -450,14 +451,14 @@ fn create_pair_component_widgets( ItemRef::Mutable(&mut *pair_data), &mut component_changed, ItemInfo { - item_title: pair_data_comp_info.name, - item_tag: &format!( + title: pair_data_comp_info.name, + tag: &format!( "world_view_entity_{}_pair_component_{pair_id}", ent_handle.uid(), ), - item_type_name: None, - item_type, - item_is_read_only: false, + type_name: None, + ty: item_type, + is_read_only: false, }, &[], ); @@ -519,7 +520,7 @@ fn create_add_component_button_widget( fn create_despawn_button_widget( frame: &dear_imgui_bindings::Ui, - textures: &Textures, + icon_textures: &IconTextures, ent_handle: &EntityHandle, actions: &mut Actions, ) @@ -541,8 +542,8 @@ fn create_despawn_button_widget( if frame .image_button_config( - &format!("ent_{}_despawn_button", ent_handle.uid()), - textures.despawn_icon_tex_id, + format!("ent_{}_despawn_button", ent_handle.uid()), + icon_textures.despawn, [14.0, 14.0], ) .build() @@ -553,7 +554,7 @@ fn create_despawn_button_widget( fn create_rename_entity_button_widget( frame: &dear_imgui_bindings::Ui, - textures: &Textures, + icon_textures: &IconTextures, ent_handle: &EntityHandle, ent_title: &str, popup_state: &mut Option<PopupState>, @@ -561,8 +562,8 @@ fn create_rename_entity_button_widget( { if frame .image_button_config( - &format!("ent_{}_rename_button", ent_handle.uid()), - textures.edit_icon_tex_id, + format!("ent_{}_rename_button", ent_handle.uid()), + icon_textures.edit, [16.0, 16.0], ) .build() @@ -716,7 +717,7 @@ fn enter_add_component_popup_state( search_text: String::with_capacity(20), searched_components: searchable_components.clone(), searchable_components, - selected_search_result: -1, + selected_search_result: None, new_component: None, })); @@ -755,15 +756,15 @@ fn show_add_component_popup( add_component_popup_state.searched_components = add_component_popup_state .searchable_components .iter() - .cloned() - .filter(|(_, searchable_comp_info, _)| { + .filter(|&(_, searchable_comp_info, _)| { searchable_comp_info .name .contains(&add_component_popup_state.search_text) }) + .cloned() .collect(); - add_component_popup_state.selected_search_result = -1; + add_component_popup_state.selected_search_result = None; } frame.spacing(); @@ -771,7 +772,7 @@ fn show_add_component_popup( let _item_width_token = frame.push_item_width(-1.0); - if frame + if let Some(selected_search_result_index) = frame .list_box_config("##add_component_popup_search_results") .build_extended( frame, @@ -793,8 +794,7 @@ fn show_add_component_popup( ) { let (selected_comp_id, selected_comp_info, _) = add_component_popup_state - .searched_components - [add_component_popup_state.selected_search_result as usize] + .searched_components[selected_search_result_index] .clone(); let Some(selected_component_type) = selected_comp_info.type_reflection @@ -844,14 +844,14 @@ fn show_add_component_popup( ItemRef::Mutable(&mut **new_component), &mut component_changed, ItemInfo { - item_title: new_component_info.name, - item_tag: &format!( + title: new_component_info.name, + tag: &format!( "add_component_popup_component_{}", new_component_info.name, ), - item_type_name: None, - item_type, - item_is_read_only: false, + type_name: None, + ty: item_type, + is_read_only: false, }, &[], ); @@ -914,11 +914,11 @@ pub enum ComponentUserCreatable struct ItemInfo<'a> { - item_title: &'a str, - item_tag: &'a str, - item_type_name: Option<&'static str>, - item_type: ItemType, - item_is_read_only: bool, + title: &'a str, + tag: &'a str, + type_name: Option<&'static str>, + ty: ItemType, + is_read_only: bool, } enum ItemType @@ -943,10 +943,10 @@ impl ItemType Self::Reflected(TypeReflection::Reference(ref_ty)) => { ItemType::Reflected(ref_ty.ty).spans_multiple_rows() } - Self::Color(..) => false, - Self::Reflected(TypeReflection::Literal(_)) | Self::String | Self::CowStr => { - false - } + Self::Reflected(TypeReflection::Literal(_)) + | Self::String + | Self::CowStr + | Self::Color(..) => false, Self::Reflected(_) => unimplemented!(), } } @@ -993,7 +993,7 @@ fn get_item_type(ty: Option<&'static TypeReflection>, type_id: TypeId) } if let Some(ty) = ty { - return Some(ItemType::Reflected(ty)); + Some(ItemType::Reflected(ty)) } else if type_id == TypeId::of::<String>() { Some(ItemType::String) } else if type_id == TypeId::of::<Cow<'static, str>>() { @@ -1086,7 +1086,7 @@ impl<Value> Deref for OwnedOrRefMut<'_, Value> fn deref(&self) -> &Self::Target { match self { - Self::Ref(value) => *value, + Self::Ref(value) => value, Self::Owned(value) => value, } } @@ -1097,7 +1097,7 @@ impl<Value> DerefMut for OwnedOrRefMut<'_, Value> fn deref_mut(&mut self) -> &mut Self::Target { match self { - Self::Ref(value) => *value, + Self::Ref(value) => value, Self::Owned(value) => value, } } @@ -1111,7 +1111,7 @@ fn create_item_title_widget( ) { if !matches!(item_type, ItemType::Reflected(TypeReflection::Reference(_))) { - frame.text(&item_title); + frame.text(item_title); if let Some(item_type_name) = item_type_name { if frame.is_item_hovered() { @@ -1126,11 +1126,11 @@ fn add_item_to_frame<'a>( mut item: ItemRef<'_>, data_changed: &mut bool, ItemInfo { - item_title, - item_tag, - item_type_name, - item_type, - item_is_read_only, + title: item_title, + tag: item_tag, + type_name: item_type_name, + ty: item_type, + is_read_only: item_is_read_only, }: ItemInfo<'a>, prev_item_tags: &[&'a str], ) @@ -1176,85 +1176,85 @@ fn add_item_to_frame<'a>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::I8 => create_scalar_item_input::<i8>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::U16 => create_scalar_item_input::<u16>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::I16 => create_scalar_item_input::<i16>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::U32 => create_scalar_item_input::<u32>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::I32 => create_scalar_item_input::<i32>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::U64 => create_scalar_item_input::<u64>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::I64 => create_scalar_item_input::<i64>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::F32 => create_scalar_item_input::<f32>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::F64 => create_scalar_item_input::<f64>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::Usize => create_scalar_item_input::<usize>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::Isize => create_scalar_item_input::<isize>( frame, &mut item, data_changed, - &item_tag, - &prev_item_tags, + item_tag, + prev_item_tags, ), LiteralType::U128 => { let item = match item { @@ -1315,7 +1315,7 @@ fn add_item_to_frame<'a>( }; if frame.checkbox( - create_item_label(&item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), item, ) { *data_changed = true; @@ -1383,11 +1383,11 @@ fn add_item_to_frame<'a>( item_item, data_changed, ItemInfo { - item_title: array_item_name.as_ref(), - item_tag: array_item_name.as_ref(), - item_type_name: Some(array_type.item_type_name()), - item_type: array_item_type, - item_is_read_only, + title: array_item_name.as_ref(), + tag: array_item_name.as_ref(), + type_name: Some(array_type.item_type_name()), + ty: array_item_type, + is_read_only: item_is_read_only, }, &prev_item_tags, ); @@ -1428,11 +1428,11 @@ fn add_item_to_frame<'a>( ItemRef::Immutable(item_item), &mut slice_item_changed, ItemInfo { - item_title: &item_name, - item_tag: &item_name, - item_type_name: Some(slice_type.item_type_name()), - item_type: slice_item_type, - item_is_read_only: true, + title: &item_name, + tag: &item_name, + type_name: Some(slice_type.item_type_name()), + ty: slice_item_type, + is_read_only: true, }, &prev_item_tags, ); @@ -1463,11 +1463,11 @@ fn add_item_to_frame<'a>( ItemRef::Immutable(derefed_item), &mut derefed_changed, ItemInfo { - item_title, - item_tag: "derefed".into(), - item_type_name, - item_type: ref_item_type, - item_is_read_only: true, + title: item_title, + tag: "derefed", + type_name: item_type_name, + ty: ref_item_type, + is_read_only: true, }, prev_item_tags, ); @@ -1494,7 +1494,7 @@ fn add_item_to_frame<'a>( if frame .input_text( - create_item_label(&item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), item, ) .build() @@ -1522,7 +1522,7 @@ fn add_item_to_frame<'a>( if frame .input_text( - create_item_label(&item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), item.to_mut(), ) .build() @@ -1537,7 +1537,7 @@ fn add_item_to_frame<'a>( if frame .color_edit3_config( - create_item_label(&item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), &mut scratch, ) .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex) @@ -1554,14 +1554,14 @@ fn add_item_to_frame<'a>( let mut item = item.into_writable::<Rgb<u8>>(); let mut scratch = [ - item.r as f32 / 255.0, - item.g as f32 / 255.0, - item.b as f32 / 255.0, + f32::from(item.r) / 255.0, + f32::from(item.g) / 255.0, + f32::from(item.b) / 255.0, ]; if frame .color_edit3_config( - create_item_label(&item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), &mut scratch, ) .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex) @@ -1570,9 +1570,9 @@ fn add_item_to_frame<'a>( let [new_r, new_g, new_b] = scratch; *item = Rgb { - r: (new_r * 255.0) as u8, - g: (new_g * 255.0) as u8, - b: (new_b * 255.0) as u8, + r: float_to_u8_checked(new_r * 255.0).unwrap_or(0), + g: float_to_u8_checked(new_g * 255.0).unwrap_or(0), + b: float_to_u8_checked(new_b * 255.0).unwrap_or(0), }; *data_changed = true; @@ -1585,7 +1585,7 @@ fn add_item_to_frame<'a>( if frame .color_edit4_config( - create_item_label(&item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), &mut scratch, ) .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex) @@ -1607,15 +1607,15 @@ fn add_item_to_frame<'a>( let mut item = item.into_writable::<Rgba<u8>>(); let mut scratch = [ - item.r as f32 / 255.0, - item.g as f32 / 255.0, - item.b as f32 / 255.0, - item.a as f32 / 255.0, + f32::from(item.r) / 255.0, + f32::from(item.g) / 255.0, + f32::from(item.b) / 255.0, + f32::from(item.a) / 255.0, ]; if frame .color_edit4_config( - create_item_label(&item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), &mut scratch, ) .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex) @@ -1624,17 +1624,16 @@ fn add_item_to_frame<'a>( let [new_r, new_g, new_b, new_a] = scratch; *item = Rgba { - r: (new_r * 255.0) as u8, - g: (new_g * 255.0) as u8, - b: (new_b * 255.0) as u8, - a: (new_a * 255.0) as u8, + r: float_to_u8_checked(new_r * 255.0).unwrap_or(0), + g: float_to_u8_checked(new_g * 255.0).unwrap_or(0), + b: float_to_u8_checked(new_b * 255.0).unwrap_or(0), + a: float_to_u8_checked(new_a * 255.0).unwrap_or(0), }; *data_changed = true; } } - ItemType::Color(ColorItemType::Rgb, _) => unreachable!(), - ItemType::Color(ColorItemType::Rgba, _) => unreachable!(), + ItemType::Color(ColorItemType::Rgb | ColorItemType::Rgba, _) => unreachable!(), ItemType::Reflected(_) => unimplemented!(), } } @@ -1667,7 +1666,7 @@ fn create_scalar_item_input<Scalar>( if frame .input_scalar( - create_item_label(item_tag, "value_input", &prev_item_tags), + create_item_label(item_tag, "value_input", prev_item_tags), item, ) .build() @@ -1682,7 +1681,7 @@ fn item_layout_table<'frame>( ) -> dear_imgui_bindings::TableBuilder<'frame> { frame - .table(create_item_label("item_layout", "table", &prev_item_tags)) + .table(create_item_label("item_layout", "table", prev_item_tags)) .headers(false) .sizing_policy(dear_imgui_bindings::TableSizingPolicy::StretchSame) .columns([ @@ -1727,8 +1726,7 @@ fn create_struct_widgets( let field_name = field .name - .map(Cow::Borrowed) - .unwrap_or_else(|| field_index.to_string().into()); + .map_or_else(|| field_index.to_string().into(), Cow::Borrowed); let Ok(field_item) = item.get_struct_field(struct_ty, field_index) else { unreachable!(); @@ -1748,11 +1746,11 @@ fn create_struct_widgets( field_item, data_changed, ItemInfo { - item_title: field_name.as_ref(), - item_tag: field_name.as_ref(), - item_type_name: Some(field.type_name()), - item_type: field_item_type, - item_is_read_only: item_is_read_only + title: field_name.as_ref(), + tag: field_name.as_ref(), + type_name: Some(field.type_name()), + ty: field_item_type, + is_read_only: item_is_read_only || matches!( field.visibility, Visibility::Private | Visibility::PubScoped(_) @@ -1768,8 +1766,7 @@ fn create_struct_widgets( { let field_name = field .name - .map(Cow::Borrowed) - .unwrap_or_else(|| field_index.to_string().into()); + .map_or_else(|| field_index.to_string().into(), Cow::Borrowed); let Ok(field_item) = item.get_struct_field(struct_ty, field_index) else { unreachable!(); @@ -1789,11 +1786,11 @@ fn create_struct_widgets( field_item, data_changed, ItemInfo { - item_title: field_name.as_ref(), - item_tag: field_name.as_ref(), - item_type_name: Some(field.type_name()), - item_type: field_item_type, - item_is_read_only: item_is_read_only + title: field_name.as_ref(), + tag: field_name.as_ref(), + type_name: Some(field.type_name()), + ty: field_item_type, + is_read_only: item_is_read_only || matches!( field.visibility, Visibility::Private | Visibility::PubScoped(_) @@ -1821,7 +1818,7 @@ fn create_enum_widgets( if create_combo_box( frame, - create_item_label(&item_tag, "variant_select", &prev_item_tags), + create_item_label(item_tag, "variant_select", prev_item_tags), &mut curr_variant_index, enum_type.variants, |variant| { @@ -1849,8 +1846,7 @@ fn create_enum_widgets( disabled: disabled.is_some(), hover_tooltip: disabled.map(|(bad_field_name, bad_field_reason)| { format!( - "Variant cannot be selected since field {} {}", - bad_field_name, bad_field_reason + "Variant cannot be selected since field {bad_field_name} {bad_field_reason}" ) .into() }), @@ -1869,8 +1865,7 @@ fn create_enum_widgets( &mut new_variant .fields .iter() - .map(|fields| fields.fields()) - .flatten() + .flat_map(engine_reflection::EnumVariantFields::fields) .map(|field| { let Some(field_type) = field.type_reflection() else { // Variants with any field that is missing type reflection is @@ -1946,11 +1941,11 @@ fn create_enum_widgets( field_item, data_changed, ItemInfo { - item_title: field_name.as_ref(), - item_tag: field_name.as_ref(), - item_type_name: Some(field.type_name()), - item_type: field_item_type, - item_is_read_only: item_is_read_only, + title: field_name.as_ref(), + tag: field_name.as_ref(), + type_name: Some(field.type_name()), + ty: field_item_type, + is_read_only: item_is_read_only, }, &prev_item_tags, ); @@ -1986,11 +1981,11 @@ fn create_enum_widgets( field_item, data_changed, ItemInfo { - item_title: field_name.as_ref(), - item_tag: field_name.as_ref(), - item_type_name: Some(field.type_name()), - item_type: field_item_type, - item_is_read_only: item_is_read_only, + title: field_name.as_ref(), + tag: field_name.as_ref(), + type_name: Some(field.type_name()), + ty: field_item_type, + is_read_only: item_is_read_only, }, &prev_item_tags, ); @@ -2003,7 +1998,7 @@ fn create_item_label(item_tag: &str, value_name: &str, prev_item_tags: &[&str]) let mut item_label = String::with_capacity( 2 + item_tag.len() + prev_item_tags.len() - + prev_item_tags.iter().max().unwrap_or(&"".into()).len(), + + prev_item_tags.iter().max().unwrap_or(&"").len(), ); item_label.push_str("##"); @@ -2052,7 +2047,11 @@ fn get_whole_type_is_user_editable( } ItemType::Reflected(TypeReflection::Enum(enum_ty)) => { enum_ty.variants.iter().any(|variant| { - for field in variant.fields.iter().flat_map(|fields| fields.fields()) { + for field in variant + .fields + .iter() + .flat_map(engine_reflection::EnumVariantFields::fields) + { if !get_whole_type_is_user_editable( field.type_reflection(), field.type_id, @@ -2103,8 +2102,7 @@ where label, preview_label .as_ref() - .map(|preview_label| preview_label.as_ref()) - .unwrap_or(""), + .map_or("", std::convert::AsRef::as_ref), ) { for (idx, item) in items.iter().enumerate() { let is_selected = idx == *current_item; @@ -2157,10 +2155,10 @@ trait ListBoxExt fn build_extended<Item, ItemFn>( self, ui: &dear_imgui_bindings::Ui, - current_item: &mut i32, + current_item: &mut Option<usize>, items: &[Item], item_fn: &ItemFn, - ) -> bool + ) -> Option<usize> where for<'b> ItemFn: Fn(&'b Item) -> ListBoxItem<'b>; } @@ -2172,32 +2170,24 @@ where fn build_extended<Item, ItemFn>( self, ui: &dear_imgui_bindings::Ui, - current_item: &mut i32, + current_item: &mut Option<usize>, items: &[Item], item_fn: &ItemFn, - ) -> bool + ) -> Option<usize> where for<'b> ItemFn: Fn(&'b Item) -> ListBoxItem<'b>, { - let mut result = false; - - let lb = self; - - if let Some(_cb) = lb.begin(ui) { - for (idx, item) in items.iter().enumerate() { - if idx > i32::MAX as usize { - break; - } - - let idx_i32 = idx as i32; + let mut result = None; + if let Some(_lb_token) = self.begin(ui) { + for (index, item) in items.iter().enumerate() { let ListBoxItem { label: item_label, disabled: item_disabled, hover_tooltip: item_hover_tooltip, } = item_fn(item); - let is_selected = idx_i32 == *current_item; + let is_selected = Some(index) == *current_item; let disabled_token = ui.begin_disabled_with_cond(item_disabled); @@ -2206,8 +2196,8 @@ where .selected(is_selected) .build() { - *current_item = idx_i32; - result = true; + *current_item = Some(index); + result = Some(index); } disabled_token.end(); @@ -2234,24 +2224,23 @@ fn create_entity_title(ent_handle: &EntityHandle) -> String "{} {}", ent_name .as_ref() - .map(|ent_name| ent_name.name.as_ref()) - .unwrap_or("<unnamed>"), + .map_or("<unnamed>", |ent_name| ent_name.name.as_ref()), ent_handle.uid() ) } -struct Textures +struct IconTextures { - despawn_icon_tex_id: dear_imgui_bindings::ManagedTextureId, - warning_icon_tex_id: dear_imgui_bindings::ManagedTextureId, - edit_icon_tex_id: dear_imgui_bindings::ManagedTextureId, + despawn: dear_imgui_bindings::ManagedTextureId, + warning: dear_imgui_bindings::ManagedTextureId, + edit: dear_imgui_bindings::ManagedTextureId, } -impl Textures +impl IconTextures { fn new(dear_imgui_context: &mut DearImguiContext) -> Self { - let despawn_icon_tex_id = create_texture( + let despawn = create_texture( dear_imgui_context, Image::from_reader( Cursor::new(include_bytes!("../../../res/ui/delete.png")), @@ -2260,7 +2249,7 @@ impl Textures .unwrap(), ); - let warning_icon_tex_id = create_texture( + let warning = create_texture( dear_imgui_context, Image::from_reader( Cursor::new(include_bytes!("../../../res/ui/warning.png")), @@ -2269,7 +2258,7 @@ impl Textures .unwrap(), ); - let edit_icon_tex_id = create_texture( + let edit = create_texture( dear_imgui_context, Image::from_reader( Cursor::new(include_bytes!("../../../res/ui/edit.png")), @@ -2278,11 +2267,7 @@ impl Textures .unwrap(), ); - Self { - despawn_icon_tex_id, - warning_icon_tex_id, - edit_icon_tex_id, - } + Self { despawn, warning, edit } } } @@ -2294,7 +2279,7 @@ struct AddComponentPopupState search_text: String, searched_components: Vec<(Uid, ComponentInfo, ComponentUserCreatable)>, searchable_components: Vec<(Uid, ComponentInfo, ComponentUserCreatable)>, - selected_search_result: i32, + selected_search_result: Option<usize>, new_component: Option<(Box<dyn Any>, ComponentInfo, Uid)>, } @@ -2312,3 +2297,20 @@ enum PopupState AddComponent(AddComponentPopupState), RenameEntity(RenameEntityPopupState), } + +fn float_to_u8_checked(value: f32) -> Option<u8> +{ + if !value.is_finite() { + return None; + } + + if value > f32::from(u8::MAX) { + return Some(u8::MAX); + } + + if value < f32::from(u8::MIN) { + return Some(u8::MIN); + } + + Some(unsafe { value.to_int_unchecked::<u8>() }) +} diff --git a/engine/src/util.rs b/engine/src/util.rs index f016ad3..61a8613 100644 --- a/engine/src/util.rs +++ b/engine/src/util.rs @@ -6,7 +6,7 @@ pub trait OptionExt<T> { /// Substitute for the currently experimental function /// [`Option::get_or_try_insert_with`]. - /// See https://github.com/rust-lang/rust/issues/143648 + /// See <https://github.com/rust-lang/rust/issues/143648> fn get_or_try_insert_with_fn<Err>( &mut self, func: impl Fn() -> Result<T, Err>, @@ -20,7 +20,7 @@ impl<T> OptionExt<T> for Option<T> func: impl FnOnce() -> Result<T, Err>, ) -> Result<&mut T, Err> { - if let None = self { + if self.is_none() { *self = Some(func()?); } @@ -160,7 +160,7 @@ impl<const BITS_PER_ITEM: usize> Iterator for BitArrayOccupiedIter<'_, BITS_PER_ self.mask &= (!Self::ITEM_MASK) << item_bit_index_in_byte; - let item_index_in_byte = item_bit_index_in_byte as usize / BITS_PER_ITEM; + let item_index_in_byte = item_bit_index_in_byte / BITS_PER_ITEM; let prev_bytes_item_cnt = (byte_index * 8) / BITS_PER_ITEM; @@ -220,7 +220,7 @@ impl<const BITS_PER_ITEM: usize> StreamingIterator self.mask &= (!Self::ITEM_MASK) << item_bit_index_in_byte; - let item_index_in_byte = item_bit_index_in_byte as usize / BITS_PER_ITEM; + let item_index_in_byte = item_bit_index_in_byte / BITS_PER_ITEM; let prev_bytes_item_cnt = (byte_index * 8) / BITS_PER_ITEM; @@ -243,7 +243,7 @@ pub struct BitArrayItemMut<'a, const BITS_PER_ITEM: usize> bit_index_in_byte: usize, } -impl<'a, const BITS_PER_ITEM: usize> BitArrayItemMut<'a, BITS_PER_ITEM> +impl<const BITS_PER_ITEM: usize> BitArrayItemMut<'_, BITS_PER_ITEM> { const ITEM_MASK: u8 = !(u8::MAX << BITS_PER_ITEM); diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs index 2493851..3e27075 100644 --- a/engine/src/windowing.rs +++ b/engine/src/windowing.rs @@ -24,7 +24,6 @@ use winit::window::{Window as WinitWindow, WindowId as WinitWindowId}; use crate::ecs::actions::Actions; use crate::ecs::component::Component; -use crate::ecs::entity::obtainer::Obtainer as EntityObtainer; use crate::ecs::event::component::{Added, Changed, EventMatchExt, Removed}; use crate::ecs::pair::{ChildOf, Pair}; use crate::ecs::phase::{Phase, PRE_UPDATE as PRE_UPDATE_PHASE}; @@ -32,11 +31,11 @@ use crate::ecs::sole::Single; use crate::ecs::system::observer::Observe; use crate::ecs::uid::Uid; use crate::ecs::util::StreamingIterator; -use crate::ecs::{declare_entity, pair, Query, Sole}; +use crate::ecs::{declare_entity, pair, Query, Sole, World}; use crate::util::BitArray; use crate::vector::Vec2; -use crate::windowing::dpi::{PhysicalPosition, PhysicalSize, Position}; -use crate::windowing::keyboard::{Key, KeyState, Keyboard, UnknownKeyCodeError}; +use crate::windowing::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize, Position}; +use crate::windowing::keyboard::{Key, KeyState, Keyboard}; use crate::windowing::monitor::Handle as MonitorHandle; use crate::windowing::mouse::{ Button as MouseButton, @@ -82,7 +81,7 @@ impl crate::ecs::extension::Extension for Extension fn collect(self, mut collector: crate::ecs::extension::Collector<'_>) { if !CONTEXT_CREATED.load(Ordering::Relaxed) { - collector.add_sole(Context::new()).ok(); + collector.add_sole(Context::new_global()).ok(); } collector.add_sole(Keyboard::default()).ok(); @@ -135,118 +134,55 @@ fn update_stuff( mut mouse: Single<Mouse>, mut mouse_buttons: Single<MouseButtons>, mut actions: Actions, - entity_obtainer: EntityObtainer, + world: &World, ) -> Result<(), EngineError> { let Ok(context) = context.get_mut() else { unreachable!(); }; - let keyboard = keyboard.get_mut()?; - let mouse = mouse.get_mut()?; - let mouse_buttons = mouse_buttons.get_mut()?; - if context.display_handle.is_none() { cold_path(); actions.stop(); return Ok(()); } - keyboard.make_key_states_previous(); - - { - let Some(mut input) = context - .shared_state - .input - .try_lock_for(Duration::from_millis(100)) - else { - tracing::error!("Locking input mutex timed out after 100ms"); - return Ok(()); - }; - - mouse.curr_tick_position_delta = input.relative_mouse_pos_delta; - mouse.position = input.absolute_mouse_pos.clone(); - mouse.curr_tick_scroll_delta = input.mouse_scroll_delta.clone(); - - for (mouse_button, mouse_button_input) in input.mouse_buttons.iter_mut() { - mouse_buttons.set_previous_to_current(mouse_button); - - if mouse_button_input.flags.is_all() { - match mouse_buttons.get_previous(mouse_button) { - MouseButtonState::Pressed => { - mouse_buttons.set(mouse_button, MouseButtonState::Released); - - mouse_button_input.flags.remove(MouseButtonFlags::RELEASED); - } - MouseButtonState::Released => { - mouse_buttons.set(mouse_button, MouseButtonState::Pressed); - - mouse_button_input.flags.remove(MouseButtonFlags::PRESSED); - } - } - - continue; - } - - let mouse_button_state = if mouse_button_input.flags.is_empty() { - let Some(mouse_button_state) = mouse_button_input.lagged.pop_front() - else { - continue; - }; - - mouse_button_state - } else { - bitflags_match!(mouse_button_input.flags, { - MouseButtonFlags::PRESSED => MouseButtonState::Pressed, - MouseButtonFlags::RELEASED => MouseButtonState::Released, - _ => unreachable!() - }) - }; - - mouse_buttons.set(mouse_button, mouse_button_state); - - mouse_button_input.flags.clear(); - } - - let mut key_updates = input.keys.iter_occupied_mut(); - - while let Some(mut key_item) = key_updates.streaming_next() { - let key = Key::KEYS[key_item.index]; - - if key_item.bits == KEY_PRESSED_BITS | KEY_RELEASED_BITS { - match keyboard.get_key_state(key) { - KeyState::Pressed => { - keyboard.set_key_state(key, KeyState::Released); + let keyboard = keyboard.get_mut()?; + let mouse = mouse.get_mut()?; + let mouse_buttons = mouse_buttons.get_mut()?; - key_item.clear_and_set(KEY_PRESSED_BITS, u8::MAX); - } - KeyState::Released => { - keyboard.set_key_state(key, KeyState::Pressed); + update_inputs(context, keyboard, mouse, mouse_buttons); - key_item.clear_and_set(KEY_RELEASED_BITS, u8::MAX); - } - } + receive_app_messages(context, &mut actions, world); - continue; - } + if context.shared_state.thread_panicked.load(Ordering::Relaxed) { + cold_path(); + return Err(error!("Windowing app thread panicked")); + } - let key_state = match key_item.bits { - KEY_PRESSED_BITS => KeyState::Pressed, - KEY_RELEASED_BITS => KeyState::Released, - _ => unreachable!(), - }; + if context.shared_state.has_thread_err.load(Ordering::Relaxed) { + cold_path(); - keyboard.set_key_state(key, key_state); + let Ok(mut err) = context.shared_state.thread_err.try_lock() else { + // The windowing app thread always unlocks the thread_err mutex before + // setting the has_thread_err atomic + unreachable!(); + }; - key_item.clear_and_set(0, u8::MAX); - } + let Some(err) = err.take() else { + // The has_thread_err atomic is set to true so this option is always + // Some + unreachable!(); + }; - input.relative_mouse_pos_delta = Vec2 { x: 0.0, y: 0.0 }; - input.mouse_scroll_delta = MouseScrollDelta { vert_lines: 0.0, hor_lines: 0.0 }; - }; + return Err(err.into()); + } - keyboard.set_text_keys(iter_array_queue(&context.shared_state.text_keys)); + Ok(()) +} +fn receive_app_messages(context: &mut Context, actions: &mut Actions, world: &World) +{ let Context { ref mut windows, ref shared_state, .. } = *context; @@ -255,11 +191,11 @@ fn update_stuff( tracing::trace!(message=?message, "Received message from app"); match message { - MessageFromApp::WindowCreated( + MessageFromApp::Window(WindowMessage::Created( window_ent_id, winit_window, window_creation_attrs, - ) => { + )) => { actions.add_components( window_ent_id, (Window::new(&winit_window, &window_creation_attrs),), @@ -277,7 +213,10 @@ fn update_stuff( "Window creation completed" ); } - MessageFromApp::WindowResized(window_id, new_window_size) => { + MessageFromApp::Window(WindowMessage::Resized( + window_id, + new_window_size, + )) => { tracing::trace!( window_id = ?window_id, "Received window resized message" @@ -293,7 +232,7 @@ fn update_stuff( continue; }; - let Some(window_ent) = entity_obtainer.get_entity(*window_ent_id) else { + let Some(window_ent) = world.get_entity(*window_ent_id) else { continue; }; @@ -305,7 +244,7 @@ fn update_stuff( window.set_changed(); } - MessageFromApp::WindowCloseRequested(window_id) => { + MessageFromApp::Window(WindowMessage::CloseRequested(window_id)) => { let Some(window_ent_id) = windows.get(window_id).map(|(_, ent_id)| ent_id) else { @@ -318,7 +257,10 @@ fn update_stuff( actions.remove_comps::<(Window,)>(*window_ent_id); } - MessageFromApp::WindowScaleFactorChanged(window_id, scale_factor) => { + MessageFromApp::Window(WindowMessage::ScaleFactorChanged( + window_id, + scale_factor, + )) => { let Some(window_ent_id) = windows.get(window_id).map(|(_, ent_id)| ent_id) else { @@ -329,7 +271,7 @@ fn update_stuff( continue; }; - let Some(window_ent) = entity_obtainer.get_entity(*window_ent_id) else { + let Some(window_ent) = world.get_entity(*window_ent_id) else { continue; }; @@ -343,31 +285,109 @@ fn update_stuff( } } } +} - if shared_state.thread_panicked.load(Ordering::Relaxed) { - cold_path(); - return Err(error!("Windowing app thread panicked")); - } - - if shared_state.has_thread_err.load(Ordering::Relaxed) { - cold_path(); +fn update_inputs( + context: &Context, + keyboard: &mut Keyboard, + mouse: &mut Mouse, + mouse_buttons: &mut MouseButtons, +) +{ + keyboard.make_key_states_previous(); - let Ok(mut err) = shared_state.thread_err.try_lock() else { - // The windowing app thread always unlocks the thread_err mutex before - // setting the has_thread_err atomic - unreachable!(); + { + let Some(mut input) = context + .shared_state + .input + .try_lock_for(Duration::from_millis(100)) + else { + tracing::error!("Locking input mutex timed out after 100ms"); + return; }; - let Some(err) = err.take() else { - // The has_thread_err atomic is set to true so this option is always - // Some - unreachable!(); - }; + mouse.curr_tick_position_delta = input.relative_mouse_pos_delta; + mouse.position = input.absolute_mouse_pos; + mouse.curr_tick_scroll_delta = input.mouse_scroll_delta.clone(); - return Err(err.into()); - } + for (mouse_button, mouse_button_input) in &mut input.mouse_buttons { + mouse_buttons.set_previous_to_current(mouse_button); - Ok(()) + if mouse_button_input.flags.is_all() { + match mouse_buttons.get_previous(mouse_button) { + MouseButtonState::Pressed => { + mouse_buttons.set(mouse_button, MouseButtonState::Released); + + mouse_button_input.flags.remove(MouseButtonFlags::RELEASED); + } + MouseButtonState::Released => { + mouse_buttons.set(mouse_button, MouseButtonState::Pressed); + + mouse_button_input.flags.remove(MouseButtonFlags::PRESSED); + } + } + + continue; + } + + let mouse_button_state = if mouse_button_input.flags.is_empty() { + let Some(mouse_button_state) = mouse_button_input.lagged.pop_front() + else { + continue; + }; + + mouse_button_state + } else { + bitflags_match!(mouse_button_input.flags, { + MouseButtonFlags::PRESSED => MouseButtonState::Pressed, + MouseButtonFlags::RELEASED => MouseButtonState::Released, + _ => unreachable!() + }) + }; + + mouse_buttons.set(mouse_button, mouse_button_state); + + mouse_button_input.flags.clear(); + } + + let mut key_updates = input.keys.iter_occupied_mut(); + + while let Some(mut key_item) = key_updates.streaming_next() { + let key = Key::KEYS[key_item.index]; + + if key_item.bits == KEY_PRESSED_BITS | KEY_RELEASED_BITS { + match keyboard.get_key_state(key) { + KeyState::Pressed => { + keyboard.set_key_state(key, KeyState::Released); + + key_item.clear_and_set(KEY_PRESSED_BITS, u8::MAX); + } + KeyState::Released => { + keyboard.set_key_state(key, KeyState::Pressed); + + key_item.clear_and_set(KEY_RELEASED_BITS, u8::MAX); + } + } + + continue; + } + + let key_state = match key_item.bits { + KEY_PRESSED_BITS => KeyState::Pressed, + KEY_RELEASED_BITS => KeyState::Released, + _ => unreachable!(), + }; + + keyboard.set_key_state(key, key_state); + + key_item.clear_and_set(0, u8::MAX); + } + + input.relative_mouse_pos_delta = Vec2 { x: 0.0, y: 0.0 }; + input.mouse_scroll_delta = MouseScrollDelta { vert_lines: 0.0, hor_lines: 0.0 }; + }; + + keyboard.set_text_keys(iter_array_queue(&context.shared_state.text_keys)); } fn handle_window_changed( @@ -442,6 +462,7 @@ pub struct Context impl Context { + #[must_use] pub fn display_handle(&self) -> Option<DisplayHandle<'_>> { self.display_handle.as_ref()?.display_handle().ok() @@ -451,6 +472,7 @@ impl Context /// /// # Safety /// The Window handle must only be used with thread safe APIs. + #[must_use] pub unsafe fn get_window_as_handle( &self, window_id: &WindowId, @@ -476,6 +498,7 @@ impl Context /// Returns the last known primary monitor. This monitor may or may not exist any /// longer and it may or may not be the primary monitor any longer. + #[must_use] pub fn get_primary_monitor(&self) -> Option<&MonitorHandle> { self.primary_monitor.as_ref() @@ -509,11 +532,12 @@ impl Context /// /// # Panics /// Will panic if a windowing context have already been created. - pub fn new() -> Self + pub fn new_global() -> Self { - if CONTEXT_CREATED.swap(true, Ordering::Relaxed) { - panic!("A windowing context have already been created"); - } + assert!( + !CONTEXT_CREATED.swap(true, Ordering::Relaxed), + "A windowing context have already been created" + ); let shared_state = Arc::new(SharedState::default()); let shared_state_b = shared_state.clone(); @@ -521,58 +545,7 @@ impl Context if let Err(err) = ThreadBuilder::new() .name("windowing app".to_string()) .spawn(move || { - let shared_state_c = shared_state_b.clone(); - - match catch_unwind(move || { - let mut app = App { - shared_state: shared_state_b, - windows: IntMap::with_capacity(1), - }; - - let event_loop = match create_event_loop() { - Ok(event_loop) => event_loop, - Err(err) => { - return Err((app, AppThreadError::CreateEventLoop(err))); - } - }; - - event_loop.set_control_flow(EventLoopControlFlow::Poll); - - event_loop - .run_app(&mut app) - .map_err(|err| (app, AppThreadError::StartEventLoop(err)))?; - - Ok(()) - }) { - Ok(Ok(())) => {} - Ok(Err((app, err))) => { - { - let Ok(mut thread_err) = - app.shared_state.thread_err.try_lock() - else { - // The thread_err mutex is only locked by the main thread - // when the has_thread_err atomic is set to true, which - // it can not be set to yet - unreachable!(); - }; - - *thread_err = Some(err); - } - - app.shared_state - .has_thread_err - .store(true, Ordering::Relaxed); - - app.shared_state.init_data_cond.notify_one(); - } - Err(_) => { - shared_state_c - .thread_panicked - .store(true, Ordering::Relaxed); - - shared_state_c.init_data_cond.notify_one(); - } - }; + app_thread_main(shared_state_b); }) { tracing::error!( @@ -664,6 +637,58 @@ impl Drop for Context } } +fn app_thread_main(shared_state: Arc<SharedState>) +{ + let shared_state_c = shared_state.clone(); + + match catch_unwind(move || { + let mut app = App { + shared_state, + windows: IntMap::with_capacity(1), + }; + + let event_loop = match create_event_loop() { + Ok(event_loop) => event_loop, + Err(err) => { + return Err(AppThreadError::CreateEventLoop(err)); + } + }; + + event_loop.set_control_flow(EventLoopControlFlow::Poll); + + event_loop + .run_app(&mut app) + .map_err(AppThreadError::StartEventLoop)?; + + Ok(()) + }) { + Ok(Ok(())) => {} + Ok(Err(err)) => { + { + let Ok(mut thread_err) = shared_state_c.thread_err.try_lock() else { + // The thread_err mutex is only locked by the main thread + // when the has_thread_err atomic is set to true, which + // it can not be set to yet + unreachable!(); + }; + + *thread_err = Some(err); + } + + shared_state_c.has_thread_err.store(true, Ordering::Relaxed); + + shared_state_c.init_data_cond.notify_one(); + } + Err(_) => { + shared_state_c + .thread_panicked + .store(true, Ordering::Relaxed); + + shared_state_c.init_data_cond.notify_one(); + } + }; +} + fn create_event_loop() -> Result<EventLoop<()>, EventLoopError> { let mut event_loop_builder = EventLoop::builder(); @@ -702,10 +727,16 @@ enum AppThreadError #[derive(Debug)] enum MessageFromApp { - WindowCreated(Uid, Arc<WinitWindow>, WindowCreationAttributes), - WindowResized(WindowId, PhysicalSize<u32>), - WindowCloseRequested(WindowId), - WindowScaleFactorChanged(WindowId, f64), + Window(WindowMessage), +} + +#[derive(Debug)] +enum WindowMessage +{ + Created(Uid, Arc<WinitWindow>, WindowCreationAttributes), + Resized(WindowId, PhysicalSize<u32>), + CloseRequested(WindowId), + ScaleFactorChanged(WindowId, f64), } #[derive(Debug)] @@ -855,11 +886,11 @@ impl App ), ); - self.send_message(MessageFromApp::WindowCreated( + self.send_message(MessageFromApp::Window(WindowMessage::Created( window_ent_id, winit_window, window_creation_attrs, - )); + ))); } MessageToApp::SetWindowCursorGrabMode(window_id, cursor_grab_mode) => { let Some((_, window_settings)) = self.windows.get_mut(window_id) @@ -896,6 +927,153 @@ impl App .input .try_lock_for(Duration::from_millis(100)) } + + fn handle_keyboard_input(&self, keyboard_event: winit::event::KeyEvent) + { + if let Some(key_text) = keyboard_event + .text + .filter(|_| keyboard_event.state.is_pressed()) + { + for character in key_text.chars() { + if self.shared_state.text_keys.is_full() { + cold_path(); + tracing::warn!("Text key queue is full. Dropping oldest character"); + } + + self.shared_state.text_keys.force_push(character); + } + } + + if keyboard_event.repeat { + return; + } + + let key_code = match keyboard_event.physical_key { + PhysicalKey::Code(key_code) => key_code, + PhysicalKey::Unidentified(native_key) => { + tracing::warn!("Ignoring unidentified key: {native_key:?}"); + return; + } + }; + + let key: Key = if let Ok(key) = key_code.try_into() { + key + } else { + tracing::warn!("Ignoring key with unknown key code {key_code:?}"); + return; + }; + + let Some(mut input) = self.lock_input() else { + tracing::error!("Locking input mutex timed out after 100ms"); + return; + }; + + let key_state_bits = match keyboard_event.state.into() { + KeyState::Pressed => KEY_PRESSED_BITS, + KeyState::Released => KEY_RELEASED_BITS, + }; + + input.keys.clear_and_set(key as usize, key_state_bits, 0); + } + + fn handle_cursor_moved(&self, position: PhysicalPosition<f64>, window_id: WindowId) + { + { + let Some(mut input) = self.lock_input() else { + tracing::error!("Locking input mutex timed out after 100ms"); + return; + }; + + input.absolute_mouse_pos = position; + } + + let Some((window, window_settings)) = self.windows.get(window_id) else { + cold_path(); + return; + }; + + if window_settings.cursor_grab_mode != CursorGrabMode::Locked { + return; + } + + let Some(window) = window.upgrade() else { + cold_path(); + return; + }; + + if !window.has_focus() { + return; + } + + let window_size = window.inner_size().to_logical::<f64>(window.scale_factor()); + + if let Err(err) = window.set_cursor_position(Position::Logical(LogicalPosition { + x: window_size.width / 2.0, + y: window_size.height / 2.0, + })) { + cold_path(); + tracing::error!( + window_id=?window_id, + "Failed to lock cursor position: {:#}", + crate::Error::new(err) + ); + } + } + + fn handle_mouse_wheel(&self, delta: winit::event::MouseScrollDelta) + { + let (hor_lines, vert_lines) = match delta { + winit::event::MouseScrollDelta::LineDelta(hor_lines, vert_lines) => { + (hor_lines, vert_lines) + } + winit::event::MouseScrollDelta::PixelDelta(pos_delta) => { + let hor_lines = match pos_delta.x.partial_cmp(&0.0) { + Some(std::cmp::Ordering::Greater) => 1.0, + Some(std::cmp::Ordering::Less) => -1.0, + _ => 0.0, + }; + + let vert_lines = match pos_delta.y.partial_cmp(&0.0) { + Some(std::cmp::Ordering::Greater) => 1.0, + Some(std::cmp::Ordering::Less) => -1.0, + _ => 0.0, + }; + + (hor_lines, vert_lines) + } + }; + + let Some(mut input) = self.lock_input() else { + tracing::error!("Locking input mutex timed out after 100ms"); + return; + }; + + input.mouse_scroll_delta.hor_lines += hor_lines; + input.mouse_scroll_delta.vert_lines += vert_lines; + } + + fn handle_mouse_input(&self, button: MouseButton, button_state: MouseButtonState) + { + let Some(mut input) = self.lock_input() else { + tracing::error!("Locking input mutex timed out after 100ms"); + return; + }; + + let button_input = input + .mouse_buttons + .entry(button) + .or_insert_with(MouseButtonInput::default); + + if button_input.flags.is_all() || !button_input.lagged.is_empty() { + button_input.lagged.push_back(button_state); + return; + } + + button_input.flags.insert(match button_state { + MouseButtonState::Pressed => MouseButtonFlags::PRESSED, + MouseButtonState::Released => MouseButtonFlags::RELEASED, + }); + } } impl ApplicationHandler for App @@ -917,10 +1095,10 @@ impl ApplicationHandler for App *init_data = Some(InitData { display: event_loop.owned_display_handle(), - available_monitors: available_monitors, + available_monitors, primary_monitor: event_loop .primary_monitor() - .map(|monitor| MonitorHandle::from_winit_monitor_handle(monitor)), + .map(MonitorHandle::from_winit_monitor_handle), }); self.shared_state.init_data_cond.notify_one(); @@ -960,173 +1138,39 @@ impl ApplicationHandler for App { match event { WindowEvent::Resized(new_window_size) => { - self.send_message(MessageFromApp::WindowResized( + self.send_message(MessageFromApp::Window(WindowMessage::Resized( WindowId::from_inner(window_id), new_window_size.into(), - )); + ))); } WindowEvent::CloseRequested => { - self.send_message(MessageFromApp::WindowCloseRequested( + self.send_message(MessageFromApp::Window(WindowMessage::CloseRequested( WindowId::from_inner(window_id), - )); + ))); } WindowEvent::KeyboardInput { device_id: _, event: keyboard_event, is_synthetic: _, - } => { - if let Some(key_text) = keyboard_event - .text - .filter(|_| keyboard_event.state.is_pressed()) - { - for character in key_text.chars() { - if self.shared_state.text_keys.is_full() { - cold_path(); - tracing::warn!( - "Text key queue is full. Dropping oldest character" - ); - } - - self.shared_state.text_keys.force_push(character); - } - } - - if keyboard_event.repeat { - return; - } - - let key_code = match keyboard_event.physical_key { - PhysicalKey::Code(key_code) => key_code, - PhysicalKey::Unidentified(native_key) => { - tracing::warn!("Ignoring unidentified key: {native_key:?}"); - return; - } - }; - - let key: Key = match key_code.try_into() { - Ok(key) => key, - Err(UnknownKeyCodeError) => { - tracing::warn!("Ignoring key with unknown key code {key_code:?}"); - return; - } - }; - - let Some(mut input) = self.lock_input() else { - tracing::error!("Locking input mutex timed out after 100ms"); - return; - }; - - let key_state_bits = match keyboard_event.state.into() { - KeyState::Pressed => KEY_PRESSED_BITS, - KeyState::Released => KEY_RELEASED_BITS, - }; - - input.keys.clear_and_set(key as usize, key_state_bits, 0); - } + } => self.handle_keyboard_input(keyboard_event), WindowEvent::CursorMoved { device_id: _, position } => { - { - let Some(mut input) = self.lock_input() else { - tracing::error!("Locking input mutex timed out after 100ms"); - return; - }; - - input.absolute_mouse_pos = position.into(); - } - - let Some((window, window_settings)) = - self.windows.get(WindowId::from_inner(window_id)) - else { - cold_path(); - return; - }; - - if window_settings.cursor_grab_mode != CursorGrabMode::Locked { - return; - } - - let Some(window) = window.upgrade() else { - cold_path(); - return; - }; - - if !window.has_focus() { - return; - } - - let window_size = window.inner_size(); - - if let Err(err) = - window.set_cursor_position(Position::Physical(PhysicalPosition { - x: window_size.width as i32 / 2, - y: window_size.height as i32 / 2, - })) - { - cold_path(); - tracing::error!( - window_id=?window_id, - "Failed to lock cursor position: {:#}", - crate::Error::new(err) - ); - }; + self.handle_cursor_moved( + position.into(), + WindowId::from_inner(window_id), + ); } WindowEvent::MouseWheel { device_id: _, delta, phase: _ } => { - let (hor_lines, vert_lines) = match delta { - winit::event::MouseScrollDelta::LineDelta(hor_lines, vert_lines) => { - (hor_lines, vert_lines) - } - winit::event::MouseScrollDelta::PixelDelta(pos_delta) => { - let hor_lines = match pos_delta.x.partial_cmp(&0.0) { - Some(std::cmp::Ordering::Greater) => 1.0, - Some(std::cmp::Ordering::Less) => -1.0, - _ => 0.0, - }; - - let vert_lines = match pos_delta.y.partial_cmp(&0.0) { - Some(std::cmp::Ordering::Greater) => 1.0, - Some(std::cmp::Ordering::Less) => -1.0, - _ => 0.0, - }; - - (hor_lines, vert_lines) - } - }; - - let Some(mut input) = self.lock_input() else { - tracing::error!("Locking input mutex timed out after 100ms"); - return; - }; - - input.mouse_scroll_delta.hor_lines += hor_lines; - input.mouse_scroll_delta.vert_lines += vert_lines; + self.handle_mouse_wheel(delta); } WindowEvent::MouseInput { device_id: _, state, button } => { - let Some(mut input) = self.lock_input() else { - tracing::error!("Locking input mutex timed out after 100ms"); - return; - }; - - let button = MouseButton::from(button); - let button_state = MouseButtonState::from(state); - - let button_input = input - .mouse_buttons - .entry(button) - .or_insert_with(|| MouseButtonInput::default()); - - if button_input.flags.is_all() || !button_input.lagged.is_empty() { - button_input.lagged.push_back(button_state); - return; - } - - button_input.flags.insert(match button_state { - MouseButtonState::Pressed => MouseButtonFlags::PRESSED, - MouseButtonState::Released => MouseButtonFlags::RELEASED, - }); + self.handle_mouse_input(button.into(), state.into()); } WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer: _ } => { - self.send_message(MessageFromApp::WindowScaleFactorChanged( - WindowId::from_inner(window_id), - scale_factor, + self.send_message(MessageFromApp::Window( + WindowMessage::ScaleFactorChanged( + WindowId::from_inner(window_id), + scale_factor, + ), )); } _ => {} @@ -1141,16 +1185,13 @@ impl ApplicationHandler for App device_event: DeviceEvent, ) { - match device_event { - DeviceEvent::MouseMotion { delta } => { - let Some(mut input) = self.lock_input() else { - tracing::error!("Locking input mutex timed out after 100ms"); - return; - }; + if let DeviceEvent::MouseMotion { delta } = device_event { + let Some(mut input) = self.lock_input() else { + tracing::error!("Locking input mutex timed out after 100ms"); + return; + }; - input.relative_mouse_pos_delta += Vec2::from(delta); - } - _ => {} + input.relative_mouse_pos_delta += Vec2::from(delta); } } } diff --git a/engine/src/windowing/dpi.rs b/engine/src/windowing/dpi.rs index e3b1be1..9bc42e1 100644 --- a/engine/src/windowing/dpi.rs +++ b/engine/src/windowing/dpi.rs @@ -75,6 +75,12 @@ impl<Pixel> PhysicalPosition<Pixel> } } + /// Attempts to convert the `x` and `y` values in `source` from the type `SourcePixel` + /// to the type `Pixel`. + /// + /// # Errors + /// Returns `Err` if conversion of either `x` or `y` by their respective `TryFrom` + /// implementations fails. pub fn try_convert_from<SourcePixel>( source: PhysicalPosition<SourcePixel>, ) -> Result<Self, Pixel::Error> @@ -100,6 +106,12 @@ pub struct LogicalPosition<Pixel> impl<Pixel> LogicalPosition<Pixel> { + /// Attempts to convert the `x` and `y` values in `source` from the type `SourcePixel` + /// to the type `Pixel`. + /// + /// # Errors + /// Returns `Err` if conversion of either `x` or `y` by their respective `TryFrom` + /// implementations fails. pub fn try_convert_from<SourcePixel>( source: LogicalPosition<SourcePixel>, ) -> Result<Self, Pixel::Error> @@ -159,6 +171,12 @@ impl<Pixel> PhysicalSize<Pixel> impl<Pixel> PhysicalSize<Pixel> { + /// Attempts to convert the `width` and `height` values in `source` from the type + /// `SourcePixel` to the type `Pixel`. + /// + /// # Errors + /// Returns `Err` if conversion of either `width` or `height` by their respective + /// `TryFrom` implementations fails. pub fn try_convert_from<SourcePixel>( source: PhysicalSize<SourcePixel>, ) -> Result<Self, Pixel::Error> @@ -184,6 +202,12 @@ pub struct LogicalSize<Pixel> impl<Pixel> LogicalSize<Pixel> { + /// Attempts to convert the `width` and `height` values in `source` from the type + /// `SourcePixel` to the type `Pixel`. + /// + /// # Errors + /// Returns `Err` if conversion of either `width` or `height` by their respective + /// `TryFrom` implementations fails. pub fn try_convert_from<SourcePixel>( source: LogicalSize<SourcePixel>, ) -> Result<Self, Pixel::Error> diff --git a/engine/src/windowing/keyboard.rs b/engine/src/windowing/keyboard.rs index dffa95e..a8ec6df 100644 --- a/engine/src/windowing/keyboard.rs +++ b/engine/src/windowing/keyboard.rs @@ -14,6 +14,7 @@ impl Keyboard { /// Returns whether the given key was just pressed this frame. This function will /// return `false` if the key was also pressed the previous frame. + #[must_use] pub fn just_pressed(&self, key: Key) -> bool { self.get_key_state(key) == KeyState::Pressed @@ -22,6 +23,7 @@ impl Keyboard /// Returns whether the given key was just released this frame. This function will /// return `false` if the key was also released the previous frame. + #[must_use] pub fn just_released(&self, key: Key) -> bool { self.get_key_state(key) == KeyState::Released @@ -29,12 +31,14 @@ impl Keyboard } /// Returns whether the given key is currently pressed. + #[must_use] pub fn pressed(&self, key: Key) -> bool { self.get_key_state(key) == KeyState::Pressed } /// Returns whether the given key is currently released. + #[must_use] pub fn released(&self, key: Key) -> bool { self.get_key_state(key) == KeyState::Released @@ -66,13 +70,11 @@ impl Keyboard { let bits = self.keys.get(key as usize); - let state = match bits & KEY_CURR_PRESSED_BITS { + match bits & KEY_CURR_PRESSED_BITS { KEY_CURR_PRESSED_BITS => KeyState::Pressed, 0 => KeyState::Released, _ => unreachable!(), - }; - - state + } } #[must_use] @@ -80,15 +82,14 @@ impl Keyboard { let bits = self.keys.get(key as usize); - let state = match bits & KEY_PREV_PRESSED_BITS { + match bits & KEY_PREV_PRESSED_BITS { KEY_PREV_PRESSED_BITS => KeyState::Pressed, 0 => KeyState::Released, _ => unreachable!(), - }; - - state + } } + #[must_use] pub fn text_keys(&self) -> &str { &self.text_keys @@ -109,7 +110,7 @@ impl Keyboard pub fn make_key_states_previous(&mut self) { for byte in self.keys.bytes_mut() { - *byte = (*byte >> 1) & 0b01010101 | *byte & 0b10101010; + *byte = (*byte >> 1) & 0b0101_0101 | *byte & 0b1010_1010; } } @@ -125,7 +126,7 @@ impl Keyboard #[non_exhaustive] pub enum Key { - /// <kbd>`</kbd> on a US keyboard. This is also called a backtick or grave. + #[doc = "<kbd>`</kbd> on a US keyboard. This is also called a backtick or grave."] /// This is the <kbd>半角</kbd>/<kbd>全角</kbd>/<kbd>漢字</kbd> /// (hankaku/zenkaku/kanji) key on Japanese keyboards Backquote, @@ -243,12 +244,12 @@ pub enum Key /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>. AltLeft, /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>. - /// This is labeled <kbd>AltGr</kbd> on many keyboard layouts. + /// This is labeled <kbd>`AltGr`</kbd> on many keyboard layouts. AltRight, /// <kbd>Backspace</kbd> or <kbd>⌫</kbd>. /// Labeled <kbd>Delete</kbd> on Apple keyboards. Backspace, - /// <kbd>CapsLock</kbd> or <kbd>⇪</kbd> + /// <kbd>`CapsLock`</kbd> or <kbd>⇪</kbd> CapsLock, /// The application context menu key, which is typically found between the right /// <kbd>Super</kbd> key and the right <kbd>Control</kbd> key. @@ -276,7 +277,7 @@ pub enum Key /// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd> /// (katakana/hiragana/romaji) KanaMode, - /// Korean: HangulMode <kbd>한/영</kbd> (han/yeong) + /// Korean: `HangulMode` <kbd>한/영</kbd> (han/yeong) /// /// Japanese (Mac keyboard): <kbd>か</kbd> (kana) Lang1, @@ -306,9 +307,9 @@ pub enum Key Home, /// <kbd>Insert</kbd> or <kbd>Ins</kbd>. Not present on Apple keyboards. Insert, - /// <kbd>Page Down</kbd>, <kbd>PgDn</kbd>, or <kbd>⇟</kbd> + /// <kbd>Page Down</kbd>, <kbd>`PgDn`</kbd>, or <kbd>⇟</kbd> PageDown, - /// <kbd>Page Up</kbd>, <kbd>PgUp</kbd>, or <kbd>⇞</kbd> + /// <kbd>Page Up</kbd>, <kbd>`PgUp`</kbd>, or <kbd>⇞</kbd> PageUp, /// <kbd>↓</kbd> ArrowDown, @@ -327,7 +328,7 @@ pub enum Key Numpad1, /// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control Numpad2, - /// <kbd>3 PgDn</kbd> on a keyboard. <kbd>3 DEF</kbd> on a phone or remote control + /// <kbd>3 `PgDn`</kbd> on a keyboard. <kbd>3 DEF</kbd> on a phone or remote control Numpad3, /// <kbd>4 ←</kbd> on a keyboard. <kbd>4 GHI</kbd> on a phone or remote control Numpad4, @@ -340,15 +341,15 @@ pub enum Key Numpad7, /// <kbd>8 ↑</kbd> on a keyboard. <kbd>8 TUV</kbd> on a phone or remote control Numpad8, - /// <kbd>9 PgUp</kbd> on a keyboard. <kbd>9 WXYZ</kbd> or <kbd>9 WXY</kbd> on a phone - /// or remote control + /// <kbd>9 `PgUp`</kbd> on a keyboard. <kbd>9 WXYZ</kbd> or <kbd>9 WXY</kbd> on a + /// phone or remote control Numpad9, /// <kbd>+</kbd> NumpadAdd, /// Found on the Microsoft Natural Keyboard. NumpadBackspace, /// <kbd>C</kbd> or <kbd>A</kbd> (All Clear). Also for use with numpads that have a - /// <kbd>Clear</kbd> key that is separate from the <kbd>NumLock</kbd> key. On the + /// <kbd>Clear</kbd> key that is separate from the <kbd>`NumLock`</kbd> key. On the /// Mac, the numpad <kbd>Clear</kbd> key is encoded as [`NumLock`]. /// /// [`NumLock`]: Self::NumLock @@ -393,7 +394,7 @@ pub enum Key /// This key is typically found below the <kbd>7</kbd> key and to the left of /// the <kbd>0</kbd> key. /// - /// Use <kbd>"NumpadMultiply"</kbd> for the <kbd>*</kbd> key on + /// Use <kbd>"`NumpadMultiply`"</kbd> for the <kbd>*</kbd> key on /// numeric keypads. NumpadStar, /// <kbd>-</kbd> @@ -403,10 +404,10 @@ pub enum Key /// <kbd>Fn</kbd> This is typically a hardware key that does not generate a separate /// code. Fn, - /// <kbd>FLock</kbd> or <kbd>FnLock</kbd>. Function Lock key. Found on the Microsoft - /// Natural Keyboard. + /// <kbd>`FLock`</kbd> or <kbd>`FnLock`</kbd>. Function Lock key. Found on the + /// Microsoft Natural Keyboard. FnLock, - /// <kbd>PrtScr SysRq</kbd> or <kbd>Print Screen</kbd> + /// <kbd>`PrtScr` `SysRq`</kbd> or <kbd>Print Screen</kbd> PrintScreen, /// <kbd>Scroll Lock</kbd> ScrollLock, @@ -577,6 +578,7 @@ impl TryFrom<winit::keyboard::KeyCode> for Key { type Error = UnknownKeyCodeError; + #[allow(clippy::too_many_lines)] fn try_from(key_code: winit::keyboard::KeyCode) -> Result<Self, Self::Error> { match key_code { diff --git a/engine/src/windowing/monitor.rs b/engine/src/windowing/monitor.rs index 894448a..392489d 100644 --- a/engine/src/windowing/monitor.rs +++ b/engine/src/windowing/monitor.rs @@ -11,6 +11,7 @@ impl Handle { /// Returns a human-readable name of the monitor. #[inline] + #[must_use] pub fn name(&self) -> Option<String> { self.inner.name() @@ -18,6 +19,7 @@ impl Handle /// Returns the monitor's resolution. #[inline] + #[must_use] pub fn size(&self) -> PhysicalSize<u32> { self.inner.size().into() @@ -26,6 +28,7 @@ impl Handle /// Returns the top-left corner position of the monitor relative to the larger full /// screen area. #[inline] + #[must_use] pub fn position(&self) -> PhysicalPosition<i32> { self.inner.position().into() @@ -33,6 +36,7 @@ impl Handle /// Returns the scale factor of the underlying monitor. #[inline] + #[must_use] pub fn scale_factor(&self) -> f64 { self.inner.scale_factor() diff --git a/engine/src/windowing/mouse.rs b/engine/src/windowing/mouse.rs index 3a43e79..a5e4cc8 100644 --- a/engine/src/windowing/mouse.rs +++ b/engine/src/windowing/mouse.rs @@ -31,6 +31,7 @@ pub struct ScrollDelta impl ScrollDelta { + #[must_use] pub fn is_zero(&self) -> bool { self.vert_lines == 0.0 && self.hor_lines == 0.0 @@ -46,6 +47,7 @@ pub struct Buttons impl Buttons { + #[must_use] pub fn get(&self, button: Button) -> ButtonState { let Some(button_data) = self.map.get(&button) else { @@ -55,6 +57,7 @@ impl Buttons button_data.current_state } + #[must_use] pub fn get_previous(&self, button: Button) -> ButtonState { let Some(button_data) = self.map.get(&button) else { @@ -70,7 +73,7 @@ impl Buttons { self.map .iter() - .map(|(button, button_data)| (button.clone(), button_data.current_state)) + .map(|(button, button_data)| (*button, button_data.current_state)) } pub fn set(&mut self, button: Button, button_state: ButtonState) @@ -120,7 +123,7 @@ impl intmap::IntKey for Button Self::Middle => 2, Self::Back => 3, Self::Forward => 4, - Self::Other(other) => 5 + *other as u32, + Self::Other(other) => 5 + u32::from(*other), } } } diff --git a/engine/src/windowing/window.rs b/engine/src/windowing/window.rs index 6d7a464..ae4a36b 100644 --- a/engine/src/windowing/window.rs +++ b/engine/src/windowing/window.rs @@ -59,6 +59,7 @@ macro_rules! gen_creation_attrs_with_fn { paste::paste! { impl CreationAttributes { + #[must_use] pub fn [<with_ $field>](mut self, new: impl Into<$field_type>) -> Self { self.$field = new.into(); @@ -93,12 +94,9 @@ impl CreationAttributes .with_title(self.title.into_owned()) .with_transparent(self.transparent) .with_maximized(self.maximized) - .with_fullscreen(match self.fullscreen { - Some(Fullscreen::Borderless) => { - Some(winit::window::Fullscreen::Borderless(None)) - } - None => None, - }) + .with_fullscreen(self.fullscreen.map(|Fullscreen::Borderless| { + winit::window::Fullscreen::Borderless(None) + })) .with_visible(self.visible) .with_resizable(self.resizable) .with_window_icon(match self.icon { @@ -212,11 +210,13 @@ pub struct Window impl Window { + #[must_use] pub fn wid(&self) -> Id { self.wid } + #[must_use] pub fn scale_factor(&self) -> f64 { self.scale_factor @@ -243,11 +243,11 @@ impl Window winit_window.set_title(&self.title); winit_window.set_cursor_visible(self.cursor_visible); - let curr_inner_size = winit_window.inner_size().clone().into(); + let curr_inner_size = winit_window.inner_size().into(); - let inner_size_request_result = match winit_window.request_inner_size( - winit::dpi::Size::Physical(self.inner_size.clone().into()), - ) { + let inner_size_request_result = match winit_window + .request_inner_size(winit::dpi::Size::Physical(self.inner_size.into())) + { // The comparison of curr_inner_size is in case the user's windowing system // lies about using the requested inner size None if curr_inner_size == self.inner_size => Ok(()), diff --git a/engine/src/work_queue.rs b/engine/src/work_queue.rs index 494d2b5..765aec3 100644 --- a/engine/src/work_queue.rs +++ b/engine/src/work_queue.rs @@ -32,7 +32,7 @@ impl<UserData: Send + Sync + 'static> WorkQueue<UserData> Self { work_sender, - thread_panic: thread_panic, + thread_panic, _thread: ThreadBuilder::new() .name(name.to_string()) .spawn(move || { |
