summaryrefslogtreecommitdiff
path: root/src/gfx/state.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfx/state.rs')
-rw-r--r--src/gfx/state.rs233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/gfx/state.rs b/src/gfx/state.rs
new file mode 100644
index 0000000..9d1cfd1
--- /dev/null
+++ b/src/gfx/state.rs
@@ -0,0 +1,233 @@
+use super::util::MatrixUniform;
+use cgmath::{prelude::*, Deg, Matrix4, Rad};
+use fps_camera::{FirstPerson, FirstPersonSettings};
+use std::time::Duration;
+
+pub struct State {
+ pub surface: wgpu::Surface,
+ pub device: wgpu::Device,
+ pub queue: wgpu::Queue,
+ pub config: wgpu::SurfaceConfiguration,
+ pub fov: Rad<f32>,
+ pub view: Matrix4<f32>,
+ pub proj: Matrix4<f32>,
+ pub camera: FirstPerson,
+ pub camera_uniform: MatrixUniform,
+ pub camera_bind_group_layout: wgpu::BindGroupLayout,
+ pub depth_texture: wgpu::Texture,
+ pub depth_view: wgpu::TextureView,
+ pub depth_sampler: wgpu::Sampler,
+}
+
+impl State {
+ pub async fn new(window: &winit::window::Window) -> Self {
+ let size = window.inner_size();
+
+ let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
+ backends: wgpu::Backends::all(),
+ dx12_shader_compiler: Default::default(),
+ });
+
+ let surface = unsafe { instance.create_surface(window) }.unwrap();
+
+ let adapter = instance
+ .request_adapter(&wgpu::RequestAdapterOptions {
+ power_preference: wgpu::PowerPreference::default(),
+ compatible_surface: Some(&surface),
+ force_fallback_adapter: false,
+ })
+ .await
+ .unwrap();
+
+ let (device, queue) = adapter
+ .request_device(
+ &wgpu::DeviceDescriptor {
+ features: wgpu::Features::empty(),
+ limits: Default::default(),
+ label: None,
+ },
+ None,
+ )
+ .await
+ .unwrap();
+
+ let surface_caps = surface.get_capabilities(&adapter);
+ let surface_format = surface_caps
+ .formats
+ .iter()
+ .copied()
+ .find(|f| f.describe().srgb)
+ .unwrap_or(surface_caps.formats[0]);
+
+ let config = wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+ format: surface_format,
+ width: size.width,
+ height: size.height,
+ present_mode: surface_caps.present_modes[0],
+ alpha_mode: surface_caps.alpha_modes[0],
+ view_formats: vec![],
+ };
+
+ let (depth_texture, depth_view, depth_sampler) =
+ Self::create_depth_texture(&config, &device);
+
+ let camera = FirstPerson::new(
+ [0.0, 0.0, 0.0],
+ FirstPersonSettings {
+ speed_horizontal: 10.0,
+ speed_vertical: 10.0,
+ mouse_sensitivity_horizontal: 1.0,
+ mouse_sensitivity_vertical: 1.0,
+ },
+ );
+
+ let camera_bind_group_layout = MatrixUniform::layout(&device, "camera");
+
+ let camera_uniform = MatrixUniform::new(
+ &device,
+ &camera_bind_group_layout,
+ Matrix4::identity(),
+ "camera",
+ true,
+ );
+
+ let mut state = Self {
+ surface,
+ device,
+ queue,
+ config,
+ fov: Deg(90.0).into(),
+ proj: Matrix4::identity(),
+ view: Matrix4::identity(),
+ camera,
+ camera_uniform,
+ camera_bind_group_layout,
+ depth_texture,
+ depth_view,
+ depth_sampler,
+ };
+
+ state.resize(size);
+
+ state
+ }
+
+ pub fn create_depth_texture(
+ config: &wgpu::SurfaceConfiguration,
+ device: &wgpu::Device,
+ ) -> (wgpu::Texture, wgpu::TextureView, wgpu::Sampler) {
+ let depth_size = wgpu::Extent3d {
+ width: config.width,
+ height: config.height,
+ depth_or_array_layers: 1,
+ };
+ let depth_descriptor = wgpu::TextureDescriptor {
+ label: Some("depth texture"),
+ size: depth_size,
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: wgpu::TextureDimension::D2,
+ format: wgpu::TextureFormat::Depth32Float,
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3.
+ | wgpu::TextureUsages::TEXTURE_BINDING,
+ view_formats: &[],
+ };
+ let depth_texture = device.create_texture(&depth_descriptor);
+
+ let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
+ let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+ address_mode_u: wgpu::AddressMode::ClampToEdge,
+ address_mode_v: wgpu::AddressMode::ClampToEdge,
+ address_mode_w: wgpu::AddressMode::ClampToEdge,
+ mag_filter: wgpu::FilterMode::Linear,
+ min_filter: wgpu::FilterMode::Linear,
+ mipmap_filter: wgpu::FilterMode::Nearest,
+ compare: Some(wgpu::CompareFunction::LessEqual),
+ lod_min_clamp: 0.0,
+ lod_max_clamp: 100.0,
+ ..Default::default()
+ });
+
+ (depth_texture, depth_view, depth_sampler)
+ }
+
+ pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
+ if size.width > 0 && size.height > 0 {
+ self.config.width = size.width;
+ self.config.height = size.height;
+ self.configure_surface();
+ self.update_projection();
+ (self.depth_texture, self.depth_view, self.depth_sampler) =
+ Self::create_depth_texture(&self.config, &self.device);
+ }
+ }
+
+ pub fn configure_surface(&mut self) {
+ self.surface.configure(&self.device, &self.config);
+ }
+
+ pub fn update_projection(&mut self) {
+ self.proj = cgmath::perspective(
+ self.fov,
+ self.config.width as f32 / self.config.height as f32,
+ 0.1,
+ 100000.0,
+ );
+ }
+
+ pub fn update(&mut self, dt: Duration) {
+ let cam = self.camera.camera(dt.as_secs_f32());
+ self.camera.position = cam.position;
+ self.view = Matrix4::from(cam.orthogonal());
+
+ self.camera_uniform.set(&self.queue, self.proj * self.view);
+ }
+
+ pub fn render(&self, map: &Option<super::map::MapRender>) -> Result<(), wgpu::SurfaceError> {
+ let output = self.surface.get_current_texture()?;
+ let view = output
+ .texture
+ .create_view(&wgpu::TextureViewDescriptor::default());
+
+ let mut encoder = self
+ .device
+ .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+
+ {
+ let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+ label: None,
+ color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+ view: &view,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Clear(wgpu::Color {
+ r: 0x87 as f64 / 255.0,
+ g: 0xCE as f64 / 255.0,
+ b: 0xEB as f64 / 255.0,
+ a: 1.0,
+ }),
+ store: true,
+ },
+ })],
+ depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
+ view: &self.depth_view,
+ depth_ops: Some(wgpu::Operations {
+ load: wgpu::LoadOp::Clear(1.0),
+ store: true,
+ }),
+ stencil_ops: None,
+ }),
+ });
+
+ if let Some(map) = map.as_ref() {
+ map.render(self, &mut render_pass);
+ }
+ }
+
+ self.queue.submit(std::iter::once(encoder.finish()));
+ output.present();
+
+ Ok(())
+ }
+}