diff options
| -rw-r--r-- | engine/src/util.rs | 141 | ||||
| -rw-r--r-- | engine/src/windowing.rs | 55 | ||||
| -rw-r--r-- | engine/src/windowing/keyboard.rs | 75 | ||||
| -rw-r--r-- | res/sky_box/cubemap_0.png | bin | 0 -> 1066007 bytes | |||
| -rw-r--r-- | res/sky_box/cubemap_1.png | bin | 0 -> 1066168 bytes | |||
| -rw-r--r-- | res/sky_box/cubemap_2.png | bin | 0 -> 1064019 bytes | |||
| -rw-r--r-- | res/sky_box/cubemap_3.png | bin | 0 -> 1064790 bytes | |||
| -rw-r--r-- | res/sky_box/cubemap_4.png | bin | 0 -> 1062236 bytes | |||
| -rw-r--r-- | res/sky_box/cubemap_5.png | bin | 0 -> 1078695 bytes | |||
| -rw-r--r-- | src/main.rs | 23 |
10 files changed, 246 insertions, 48 deletions
diff --git a/engine/src/util.rs b/engine/src/util.rs index 688778b..f820b64 100644 --- a/engine/src/util.rs +++ b/engine/src/util.rs @@ -204,6 +204,147 @@ where } } +#[derive(Debug)] +pub struct BitArray<const SIZE: usize, const BITS_PER_ITEM: usize> +{ + inner: [u8; SIZE], +} + +impl<const SIZE: usize, const BITS_PER_ITEM: usize> BitArray<SIZE, BITS_PER_ITEM> +{ + const ITEM_MASK: u8 = !(u8::MAX << BITS_PER_ITEM); + + pub fn new() -> Self + { + assert!(BITS_PER_ITEM > 1); + assert!(BITS_PER_ITEM <= 8); + assert_eq!(BITS_PER_ITEM % 2, 0); + + Self { inner: [0; SIZE] } + } + + pub fn get(&self, item_index: usize) -> u8 + { + let bit_index = item_index * BITS_PER_ITEM; + + let byte_index = bit_index / 8; + + let bit_index_in_byte = bit_index - (byte_index * 8); + + (self.inner[byte_index] >> (bit_index_in_byte)) & Self::ITEM_MASK + } + + #[tracing::instrument(skip_all)] + pub fn set(&mut self, item_index: usize, item_bits: u8) + { + let item_bits = item_bits & Self::ITEM_MASK; + + let bit_index = item_index * BITS_PER_ITEM; + + let byte_index = bit_index / 8; + + let bit_index_in_byte = bit_index - (byte_index * 8); + + tracing::trace!( + item_bits, + bit_index, + byte_index, + bit_index_in_byte, + "Setting item bits" + ); + + self.inner[byte_index] &= !(Self::ITEM_MASK << bit_index_in_byte); + + self.inner[byte_index] |= item_bits << bit_index_in_byte; + } + + pub fn clear(&mut self) + { + self.inner.fill(0); + } + + pub fn iter_occupied(&self) -> BitArrayOccupiedIter<'_, BITS_PER_ITEM> + { + BitArrayOccupiedIter { + inner: self.inner.iter().copied().enumerate(), + byte: None, + mask: u8::MAX, + } + } + + pub fn bytes_mut(&mut self) -> &mut [u8] + { + &mut self.inner + } +} + +impl<const SIZE: usize, const BITS_PER_ITEM: usize> Default + for BitArray<SIZE, BITS_PER_ITEM> +{ + fn default() -> Self + { + Self::new() + } +} + +pub struct BitArrayOccupiedIter<'a, const BITS_PER_ITEM: usize> +{ + inner: std::iter::Enumerate<std::iter::Copied<std::slice::Iter<'a, u8>>>, + byte: Option<(u8, usize)>, + mask: u8, +} + +impl<const BITS_PER_ITEM: usize> BitArrayOccupiedIter<'_, BITS_PER_ITEM> +{ + const ITEM_MASK: u8 = !(u8::MAX << BITS_PER_ITEM); +} + +impl<const BITS_PER_ITEM: usize> Iterator for BitArrayOccupiedIter<'_, BITS_PER_ITEM> +{ + type Item = (usize, u8); + + fn next(&mut self) -> Option<Self::Item> + { + let (byte_masked, byte_index, item_bit_index_in_byte) = loop { + let current = self.byte.and_then(|(byte, byte_index)| { + let byte_masked = byte & self.mask; + + let lowest_one = byte_masked.lowest_one()?; + + let item_bit_index_in_byte = match lowest_one as usize % BITS_PER_ITEM { + 0 => lowest_one as usize, + remainder => lowest_one as usize - remainder, + }; + + Some((byte_masked, byte_index, item_bit_index_in_byte)) + }); + + let Some((byte_masked, byte_index, item_bit_index_in_byte)) = current else { + let (next_byte_index, next_byte) = self.inner.next()?; + + self.byte = Some((next_byte, next_byte_index)); + self.mask = u8::MAX; + + continue; + }; + + break (byte_masked, byte_index, item_bit_index_in_byte); + }; + + let item_bits = (byte_masked >> (item_bit_index_in_byte)) & Self::ITEM_MASK; + + 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 prev_bytes_item_cnt = (byte_index * 8) / BITS_PER_ITEM; + + let index = prev_bytes_item_cnt + item_index_in_byte; + + Some((index, item_bits)) + } +} + macro_rules! try_option { ($expr: expr) => { match $expr { diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs index 808e85e..805004a 100644 --- a/engine/src/windowing.rs +++ b/engine/src/windowing.rs @@ -32,7 +32,7 @@ use crate::ecs::sole::Single; use crate::ecs::system::observer::Observe; use crate::ecs::uid::Uid; use crate::ecs::{declare_entity, pair, Query, Sole}; -use crate::util::MapVec; +use crate::util::{BitArray, MapVec}; use crate::vector::Vec2; use crate::windowing::dpi::{PhysicalPosition, PhysicalSize, Position}; use crate::windowing::keyboard::{Key, KeyState, Keyboard, UnknownKeyCodeError}; @@ -207,8 +207,21 @@ fn update_stuff( mouse_button_input.flags.clear(); } + for (key_index, key_state_bits) in input.keys.iter_occupied() { + let key = Key::KEYS[key_index]; + + let key_state = match key_state_bits { + KEY_PRESSED_BITS => KeyState::Pressed, + KEY_RELEASED_BITS => KeyState::Released, + _ => unreachable!(), + }; + + keyboard.set_key_state(key, key_state); + } + 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 }; + input.keys.clear(); }; keyboard.set_text_keys(iter_array_queue(&context.shared_state.text_keys)); @@ -307,9 +320,6 @@ fn update_stuff( window.set_changed(); } - MessageFromApp::KeyboardKeyStateChanged(key, key_state) => { - keyboard.set_key_state(key, key_state); - } } } @@ -650,7 +660,9 @@ fn create_event_loop() -> Result<EventLoop<()>, EventLoopError> true, ); } - _ => { compile_error!("Unsupported platform") } + _ => { + compile_error!("Unsupported platform") + } } event_loop_builder.build() @@ -673,7 +685,6 @@ enum MessageFromApp WindowResized(WindowId, PhysicalSize<u32>), WindowCloseRequested(WindowId), WindowScaleFactorChanged(WindowId, f64), - KeyboardKeyStateChanged(Key, KeyState), } #[derive(Debug)] @@ -724,6 +735,7 @@ struct Input absolute_mouse_pos: PhysicalPosition<f64>, mouse_scroll_delta: MouseScrollDelta, mouse_buttons: MapVec<MouseButton, MouseButtonInput>, + keys: BitArray<{ (Key::KEYS.len() * BITS_PER_KEY).div_ceil(8) }, BITS_PER_KEY>, } impl Default for Input @@ -741,6 +753,7 @@ impl Default for Input (MouseButton::Back, MouseButtonInput::default()), (MouseButton::Forward, MouseButtonInput::default()), ]), + keys: BitArray::new(), } } } @@ -761,6 +774,11 @@ struct MouseButtonFlags: u8 } } +const BITS_PER_KEY: usize = 2; + +const KEY_PRESSED_BITS: u8 = 0b11; +const KEY_RELEASED_BITS: u8 = 0b01; + #[derive(Debug)] struct InitData { @@ -977,10 +995,17 @@ impl ApplicationHandler for App } }; - self.send_message(MessageFromApp::KeyboardKeyStateChanged( - key, - keyboard_event.state.into(), - )); + 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.set(key as usize, key_state_bits); } WindowEvent::CursorMoved { device_id: _, position } => { { @@ -1148,21 +1173,23 @@ fn get_monitor_native_id( target_os = "linux" => { use winit::platform::wayland::{ ActiveEventLoopExtWayland, - MonitorHandleExtWayland + MonitorHandleExtWayland, }; use winit::platform::x11::MonitorHandleExtX11; if event_loop.is_wayland() { NativeMonitorId::Wayland( - <WinitMonitorHandle as MonitorHandleExtWayland>::native_id(monitor) + <WinitMonitorHandle as MonitorHandleExtWayland>::native_id(monitor), ) } else { NativeMonitorId::X11( - <WinitMonitorHandle as MonitorHandleExtX11>::native_id(monitor) + <WinitMonitorHandle as MonitorHandleExtX11>::native_id(monitor), ) } } - _ => { compile_error!("Unsupported target platform") } + _ => { + compile_error!("Unsupported target platform") + } } } diff --git a/engine/src/windowing/keyboard.rs b/engine/src/windowing/keyboard.rs index 4515d8a..8e0cfbe 100644 --- a/engine/src/windowing/keyboard.rs +++ b/engine/src/windowing/keyboard.rs @@ -1,11 +1,12 @@ -use std::collections::HashMap; +use util_macros::VariantArr; use crate::ecs::Sole; +use crate::util::BitArray; #[derive(Debug, Default, Sole)] pub struct Keyboard { - map: HashMap<Key, KeyData>, + keys: BitArray<{ (Key::KEYS.len() * BITS_PER_KEY).div_ceil(8) }, BITS_PER_KEY>, text_keys: String, } @@ -43,33 +44,49 @@ impl Keyboard /// different state than they had the previous frame. pub fn new_key_states(&self) -> impl Iterator<Item = (Key, KeyState)> + use<'_> { - self.map.iter().filter_map(|(key, key_data)| { - if key_data.curr_state == key_data.previous_state { + self.keys.iter_occupied().filter_map(|(key_index, bits)| { + if bits == KEY_CURR_PRESSED_BITS | KEY_PREV_PRESSED_BITS { return None; } - Some((*key, key_data.curr_state)) + let key = Key::KEYS[key_index]; + + let curr_state = match bits & KEY_CURR_PRESSED_BITS { + KEY_CURR_PRESSED_BITS => KeyState::Pressed, + 0 => KeyState::Released, + _ => unreachable!(), + }; + + Some((key, curr_state)) }) } #[must_use] pub fn get_key_state(&self, key: Key) -> KeyState { - let Some(key_data) = self.map.get(&key) else { - return KeyState::Released; + let bits = self.keys.get(key as usize); + + let state = match bits & KEY_CURR_PRESSED_BITS { + KEY_CURR_PRESSED_BITS => KeyState::Pressed, + 0 => KeyState::Released, + _ => unreachable!(), }; - key_data.curr_state + state } #[must_use] pub fn get_prev_key_state(&self, key: Key) -> KeyState { - let Some(key_data) = self.map.get(&key) else { - return KeyState::Released; + let bits = self.keys.get(key as usize); + + let state = match bits & KEY_PREV_PRESSED_BITS { + KEY_PREV_PRESSED_BITS => KeyState::Pressed, + 0 => KeyState::Released, + _ => unreachable!(), }; - key_data.previous_state + state } pub fn text_keys(&self) -> &str @@ -79,15 +96,19 @@ impl Keyboard pub fn set_key_state(&mut self, key: Key, key_state: KeyState) { - let key_data = self.map.entry(key).or_default(); - - key_data.curr_state = key_state; + self.keys.set( + key as usize, + match key_state { + KeyState::Pressed => KEY_CURR_PRESSED_BITS, + KeyState::Released => 0, + }, + ); } pub fn make_key_states_previous(&mut self) { - for key_data in self.map.values_mut() { - key_data.previous_state = key_data.curr_state; + for byte in self.keys.bytes_mut() { + *byte = (*byte >> 1) & 0b01010101 | *byte & 0b10101010; } } @@ -98,7 +119,8 @@ impl Keyboard } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantArr)] +#[variant_arr(name = KEYS)] #[non_exhaustive] pub enum Key { @@ -797,20 +819,7 @@ impl From<winit::event::ElementState> for KeyState } } -#[derive(Debug)] -struct KeyData -{ - curr_state: KeyState, - previous_state: KeyState, -} +const BITS_PER_KEY: usize = 2; -impl Default for KeyData -{ - fn default() -> Self - { - KeyData { - curr_state: KeyState::Released, - previous_state: KeyState::Released, - } - } -} +const KEY_CURR_PRESSED_BITS: u8 = 0b10; +const KEY_PREV_PRESSED_BITS: u8 = 0b01; diff --git a/res/sky_box/cubemap_0.png b/res/sky_box/cubemap_0.png Binary files differnew file mode 100644 index 0000000..5f8e75c --- /dev/null +++ b/res/sky_box/cubemap_0.png diff --git a/res/sky_box/cubemap_1.png b/res/sky_box/cubemap_1.png Binary files differnew file mode 100644 index 0000000..4b4e594 --- /dev/null +++ b/res/sky_box/cubemap_1.png diff --git a/res/sky_box/cubemap_2.png b/res/sky_box/cubemap_2.png Binary files differnew file mode 100644 index 0000000..4233289 --- /dev/null +++ b/res/sky_box/cubemap_2.png diff --git a/res/sky_box/cubemap_3.png b/res/sky_box/cubemap_3.png Binary files differnew file mode 100644 index 0000000..fa84308 --- /dev/null +++ b/res/sky_box/cubemap_3.png diff --git a/res/sky_box/cubemap_4.png b/res/sky_box/cubemap_4.png Binary files differnew file mode 100644 index 0000000..c67b8f9 --- /dev/null +++ b/res/sky_box/cubemap_4.png diff --git a/res/sky_box/cubemap_5.png b/res/sky_box/cubemap_5.png Binary files differnew file mode 100644 index 0000000..eba15c4 --- /dev/null +++ b/res/sky_box/cubemap_5.png diff --git a/src/main.rs b/src/main.rs index b289332..114228e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,8 @@ use engine::rendering::{ TargetWindow as RenderingTargetWindow, }; use engine::scene::{Active as ActiveScene, Scene}; +use engine::sky_box::SkyBox; +use engine::texture::CubeMapFace; use engine::transform::WorldPosition; use engine::ui::dear_imgui::{ Context as DearImguiContext, @@ -133,7 +135,26 @@ fn init(mut assets: Single<Assets>, mut actions: Actions) -> Result<(), EngineEr ), ); - let scene_ent_id = actions.spawn_named("Scene", (Scene, ActiveScene)); + let sky_box_dir = Path::new(RESOURCE_DIR).join("sky_box"); + + let scene_ent_id = actions.spawn_named( + "Scene", + ( + Scene, + ActiveScene, + SkyBox::AssetPerCubeMapFace( + [ + (CubeMapFace::PositiveX, "cubemap_0.png"), + (CubeMapFace::NegativeX, "cubemap_1.png"), + (CubeMapFace::PositiveY, "cubemap_2.png"), + (CubeMapFace::NegativeY, "cubemap_3.png"), + (CubeMapFace::PositiveZ, "cubemap_4.png"), + (CubeMapFace::NegativeZ, "cubemap_5.png"), + ] + .map(|(face, file)| (face, assets.load(sky_box_dir.join(file)))), + ), + ), + ); actions.spawn_named( "Main camera", |
