touhou-reimpl/src/engine.rs
2024-11-03 14:23:47 -06:00

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);
}