249 lines
5.9 KiB
Rust
249 lines
5.9 KiB
Rust
use std::sync::{Arc, Mutex};
|
|
|
|
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: Arc<Device>,
|
|
pub queue: Arc<Queue>,
|
|
pub config: Arc<Mutex<SurfaceConfiguration>>,
|
|
pub size: PhysicalSize<u32>,
|
|
pub window: Arc<Window>,
|
|
|
|
keys: Keys,
|
|
state: Game,
|
|
focused: bool,
|
|
}
|
|
|
|
#[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 (_, 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, device, queue) = async_std::task::block_on(async {
|
|
let adapter = instance
|
|
.request_adapter(&RequestAdapterOptions {
|
|
compatible_surface: Some(&surface),
|
|
..Default::default()
|
|
})
|
|
.await
|
|
.unwrap();
|
|
|
|
let (device, queue) = adapter
|
|
.request_device(
|
|
&DeviceDescriptor {
|
|
label: Some("device"),
|
|
required_features: Features::empty(),
|
|
..Default::default()
|
|
},
|
|
None,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
(adapter, device, queue)
|
|
});
|
|
|
|
let (device, queue) = (Arc::new(device), Arc::new(queue));
|
|
|
|
let size = window.inner_size();
|
|
let caps = surface.get_capabilities(&adapter);
|
|
let format =
|
|
caps.formats.iter().find(|f| matches!(f, TextureFormat::Bgra8Unorm)).cloned().unwrap_or(caps.formats[0]);
|
|
let config = Arc::new(Mutex::new(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.lock().unwrap());
|
|
|
|
let keys = Keys::default();
|
|
let state = Game::new(&UpdateContext {
|
|
device: &device,
|
|
queue: &queue,
|
|
window: &window,
|
|
keys: &keys,
|
|
config: &config.clone(),
|
|
});
|
|
|
|
Self {
|
|
surface,
|
|
device,
|
|
queue,
|
|
config,
|
|
size,
|
|
window,
|
|
|
|
keys: Keys::default(),
|
|
focused: false,
|
|
|
|
state,
|
|
}
|
|
}
|
|
|
|
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;
|
|
{
|
|
let mut config = self.config.lock().unwrap();
|
|
config.width = new_size.width;
|
|
config.height = new_size.height;
|
|
self.surface.configure(&self.device, &config);
|
|
}
|
|
|
|
self.state.resize(
|
|
&UpdateContext {
|
|
device: &self.device,
|
|
queue: &self.queue,
|
|
keys: &self.keys,
|
|
window: &self.window,
|
|
config: &self.config,
|
|
},
|
|
new_size,
|
|
);
|
|
}
|
|
}
|
|
|
|
pub fn update(&mut self) {
|
|
self.state.update(&UpdateContext {
|
|
device: &self.device,
|
|
queue: &self.queue,
|
|
keys: &self.keys,
|
|
window: &self.window,
|
|
config: &self.config,
|
|
});
|
|
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.render(self, &output.texture, &mut encoder);
|
|
|
|
self.queue.submit(std::iter::once(encoder.finish()));
|
|
output.present();
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub struct UpdateContext<'a> {
|
|
pub device: &'a Arc<Device>,
|
|
pub queue: &'a Arc<Queue>,
|
|
pub keys: &'a Keys,
|
|
pub window: &'a Arc<Window>,
|
|
pub config: &'a Arc<Mutex<SurfaceConfiguration>>,
|
|
}
|
|
|
|
pub trait EngineState {
|
|
fn new(context: &UpdateContext) -> Self
|
|
where
|
|
Self: Sized;
|
|
fn update(&mut self, context: &UpdateContext);
|
|
fn resize(&mut self, context: &UpdateContext, new_size: PhysicalSize<u32>);
|
|
fn render(&self, engine: &Engine, surface: &Texture, encoder: &mut CommandEncoder);
|
|
}
|