233 lines
5.4 KiB
Rust
233 lines
5.4 KiB
Rust
|
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||
|
|
||
|
use pollster::FutureExt;
|
||
|
use wgpu::{
|
||
|
naga::FastHashMap, Backends, CommandEncoder, Device, DeviceDescriptor, Features, Instance, InstanceDescriptor, InstanceFlags, Queue, RequestAdapterOptions, Surface, SurfaceConfiguration, Texture, TextureFormat, TextureUsages
|
||
|
};
|
||
|
use winit::{
|
||
|
dpi::PhysicalSize, event::{ElementState, KeyEvent, WindowEvent}, keyboard::{KeyCode, PhysicalKey}, window::Window
|
||
|
};
|
||
|
|
||
|
use crate::game::Game;
|
||
|
|
||
|
pub struct Engine<'a> {
|
||
|
surface: Surface<'a>,
|
||
|
pub device: Device,
|
||
|
pub queue: Queue,
|
||
|
pub config: SurfaceConfiguration,
|
||
|
pub size: PhysicalSize<u32>,
|
||
|
pub window: Arc<Window>,
|
||
|
|
||
|
keys: Keys,
|
||
|
focused: bool,
|
||
|
|
||
|
state: Rc<RefCell<dyn EngineState>>,
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Default)]
|
||
|
pub enum KeyState {
|
||
|
#[default]
|
||
|
Released,
|
||
|
Pressed,
|
||
|
Held,
|
||
|
}
|
||
|
|
||
|
impl KeyState {
|
||
|
fn is_down(&self) -> bool {
|
||
|
match self {
|
||
|
Self::Released => false,
|
||
|
Self::Pressed | Self::Held => true,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Default)]
|
||
|
pub struct Keys {
|
||
|
values: FastHashMap<KeyCode, KeyState>,
|
||
|
}
|
||
|
|
||
|
impl Keys {
|
||
|
fn update_key(&mut self, key: KeyCode, state: ElementState) {
|
||
|
let key_state = self.values.entry(key).or_default();
|
||
|
if key_state.is_down() != state.is_pressed() {
|
||
|
*key_state = if state.is_pressed() {
|
||
|
KeyState::Pressed
|
||
|
} else {
|
||
|
KeyState::Released
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn tick_keys(&mut self) {
|
||
|
for (key, state) in &mut self.values {
|
||
|
if state.is_down() {
|
||
|
*state = KeyState::Held;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn is_key_down(&self, key: KeyCode) -> bool {
|
||
|
if let Some(key_state) = self.values.get(&key) {
|
||
|
key_state.is_down()
|
||
|
} else {
|
||
|
false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn was_key_released(&self, key: KeyCode) -> bool {
|
||
|
!self.is_key_down(key)
|
||
|
}
|
||
|
|
||
|
pub fn was_key_pressed(&self, key: KeyCode) -> bool {
|
||
|
matches!(self.values.get(&key), Some(KeyState::Pressed))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a> Engine<'a> {
|
||
|
pub fn new(window: Arc<Window>) -> Self {
|
||
|
let instance = Instance::new(InstanceDescriptor {
|
||
|
backends: Backends::all(),
|
||
|
flags: InstanceFlags::advanced_debugging(),
|
||
|
..Default::default()
|
||
|
});
|
||
|
|
||
|
let surface = instance.create_surface(window.clone()).unwrap();
|
||
|
|
||
|
let adapter = instance
|
||
|
.request_adapter(&RequestAdapterOptions {
|
||
|
compatible_surface: Some(&surface),
|
||
|
..Default::default()
|
||
|
})
|
||
|
.block_on()
|
||
|
.unwrap();
|
||
|
|
||
|
let (device, queue) = adapter
|
||
|
.request_device(
|
||
|
&DeviceDescriptor {
|
||
|
label: Some("device"),
|
||
|
required_features: Features::empty(),
|
||
|
..Default::default()
|
||
|
},
|
||
|
None,
|
||
|
)
|
||
|
.block_on()
|
||
|
.unwrap();
|
||
|
|
||
|
let size = window.inner_size();
|
||
|
let caps = surface.get_capabilities(&adapter);
|
||
|
println!("formats: {:#?}", caps.formats);
|
||
|
let format =
|
||
|
caps.formats.iter().find(|f| matches!(f, TextureFormat::Bgra8Unorm)).cloned().unwrap_or(caps.formats[0]);
|
||
|
let config = SurfaceConfiguration {
|
||
|
usage: TextureUsages::RENDER_ATTACHMENT,
|
||
|
format,
|
||
|
width: size.width,
|
||
|
height: size.height,
|
||
|
present_mode: caps.present_modes[0],
|
||
|
desired_maximum_frame_latency: 2,
|
||
|
alpha_mode: caps.alpha_modes[0],
|
||
|
view_formats: vec![],
|
||
|
};
|
||
|
|
||
|
surface.configure(&device, &config);
|
||
|
|
||
|
let viewer = Game::new(InitContext {
|
||
|
device: &device,
|
||
|
queue: &queue,
|
||
|
window: &window,
|
||
|
config: &config,
|
||
|
});
|
||
|
|
||
|
Self {
|
||
|
surface,
|
||
|
device,
|
||
|
queue,
|
||
|
config,
|
||
|
size,
|
||
|
window,
|
||
|
|
||
|
keys: Keys::default(),
|
||
|
focused: false,
|
||
|
|
||
|
state: Rc::new(RefCell::new(viewer)),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn handle_event(&mut self, window_event: &WindowEvent) {
|
||
|
match window_event {
|
||
|
WindowEvent::KeyboardInput {
|
||
|
event:
|
||
|
KeyEvent {
|
||
|
physical_key: PhysicalKey::Code(key),
|
||
|
state,
|
||
|
repeat,
|
||
|
..
|
||
|
},
|
||
|
..
|
||
|
} => {
|
||
|
if *repeat {
|
||
|
return;
|
||
|
}
|
||
|
self.keys.update_key(*key, *state);
|
||
|
}
|
||
|
WindowEvent::Focused(focused) => {
|
||
|
self.focused = *focused;
|
||
|
}
|
||
|
_ => {}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn resize(&mut self, new_size: PhysicalSize<u32>) {
|
||
|
if new_size.width > 0 && new_size.height > 0 {
|
||
|
self.size = new_size;
|
||
|
self.config.width = new_size.width;
|
||
|
self.config.height = new_size.height;
|
||
|
self.surface.configure(&self.device, &self.config);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn update(&mut self) {
|
||
|
self.state.borrow_mut().update(&UpdateContext {
|
||
|
device: &self.device,
|
||
|
queue: &self.queue,
|
||
|
keys: &self.keys,
|
||
|
window: &self.window,
|
||
|
});
|
||
|
self.keys.tick_keys();
|
||
|
}
|
||
|
pub fn render(&self) -> Result<(), wgpu::SurfaceError> {
|
||
|
let output = self.surface.get_current_texture()?;
|
||
|
|
||
|
let mut encoder = self.device.create_command_encoder(&Default::default());
|
||
|
|
||
|
self.state.borrow().render(self, &output.texture, &mut encoder);
|
||
|
|
||
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||
|
output.present();
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct InitContext<'a> {
|
||
|
pub device: &'a Device,
|
||
|
pub queue: &'a Queue,
|
||
|
pub window: &'a Window,
|
||
|
pub config: &'a SurfaceConfiguration,
|
||
|
}
|
||
|
|
||
|
pub struct UpdateContext<'a> {
|
||
|
pub device: &'a Device,
|
||
|
pub queue: &'a Queue,
|
||
|
pub keys: &'a Keys,
|
||
|
pub window: &'a Window,
|
||
|
}
|
||
|
|
||
|
pub trait EngineState {
|
||
|
fn new(context: InitContext) -> Self
|
||
|
where
|
||
|
Self: Sized;
|
||
|
fn update(&mut self, device: &UpdateContext);
|
||
|
fn render(&self, engine: &Engine, surface: &Texture, encoder: &mut CommandEncoder);
|
||
|
}
|