]> git.lizzy.rs Git - mt_client.git/blob - src/gfx/state.rs
9d1cfd1a5ac8fd50d5adae7d105903dbb76dbef2
[mt_client.git] / src / gfx / state.rs
1 use super::util::MatrixUniform;
2 use cgmath::{prelude::*, Deg, Matrix4, Rad};
3 use fps_camera::{FirstPerson, FirstPersonSettings};
4 use std::time::Duration;
5
6 pub struct State {
7     pub surface: wgpu::Surface,
8     pub device: wgpu::Device,
9     pub queue: wgpu::Queue,
10     pub config: wgpu::SurfaceConfiguration,
11     pub fov: Rad<f32>,
12     pub view: Matrix4<f32>,
13     pub proj: Matrix4<f32>,
14     pub camera: FirstPerson,
15     pub camera_uniform: MatrixUniform,
16     pub camera_bind_group_layout: wgpu::BindGroupLayout,
17     pub depth_texture: wgpu::Texture,
18     pub depth_view: wgpu::TextureView,
19     pub depth_sampler: wgpu::Sampler,
20 }
21
22 impl State {
23     pub async fn new(window: &winit::window::Window) -> Self {
24         let size = window.inner_size();
25
26         let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
27             backends: wgpu::Backends::all(),
28             dx12_shader_compiler: Default::default(),
29         });
30
31         let surface = unsafe { instance.create_surface(window) }.unwrap();
32
33         let adapter = instance
34             .request_adapter(&wgpu::RequestAdapterOptions {
35                 power_preference: wgpu::PowerPreference::default(),
36                 compatible_surface: Some(&surface),
37                 force_fallback_adapter: false,
38             })
39             .await
40             .unwrap();
41
42         let (device, queue) = adapter
43             .request_device(
44                 &wgpu::DeviceDescriptor {
45                     features: wgpu::Features::empty(),
46                     limits: Default::default(),
47                     label: None,
48                 },
49                 None,
50             )
51             .await
52             .unwrap();
53
54         let surface_caps = surface.get_capabilities(&adapter);
55         let surface_format = surface_caps
56             .formats
57             .iter()
58             .copied()
59             .find(|f| f.describe().srgb)
60             .unwrap_or(surface_caps.formats[0]);
61
62         let config = wgpu::SurfaceConfiguration {
63             usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
64             format: surface_format,
65             width: size.width,
66             height: size.height,
67             present_mode: surface_caps.present_modes[0],
68             alpha_mode: surface_caps.alpha_modes[0],
69             view_formats: vec![],
70         };
71
72         let (depth_texture, depth_view, depth_sampler) =
73             Self::create_depth_texture(&config, &device);
74
75         let camera = FirstPerson::new(
76             [0.0, 0.0, 0.0],
77             FirstPersonSettings {
78                 speed_horizontal: 10.0,
79                 speed_vertical: 10.0,
80                 mouse_sensitivity_horizontal: 1.0,
81                 mouse_sensitivity_vertical: 1.0,
82             },
83         );
84
85         let camera_bind_group_layout = MatrixUniform::layout(&device, "camera");
86
87         let camera_uniform = MatrixUniform::new(
88             &device,
89             &camera_bind_group_layout,
90             Matrix4::identity(),
91             "camera",
92             true,
93         );
94
95         let mut state = Self {
96             surface,
97             device,
98             queue,
99             config,
100             fov: Deg(90.0).into(),
101             proj: Matrix4::identity(),
102             view: Matrix4::identity(),
103             camera,
104             camera_uniform,
105             camera_bind_group_layout,
106             depth_texture,
107             depth_view,
108             depth_sampler,
109         };
110
111         state.resize(size);
112
113         state
114     }
115
116     pub fn create_depth_texture(
117         config: &wgpu::SurfaceConfiguration,
118         device: &wgpu::Device,
119     ) -> (wgpu::Texture, wgpu::TextureView, wgpu::Sampler) {
120         let depth_size = wgpu::Extent3d {
121             width: config.width,
122             height: config.height,
123             depth_or_array_layers: 1,
124         };
125         let depth_descriptor = wgpu::TextureDescriptor {
126             label: Some("depth texture"),
127             size: depth_size,
128             mip_level_count: 1,
129             sample_count: 1,
130             dimension: wgpu::TextureDimension::D2,
131             format: wgpu::TextureFormat::Depth32Float,
132             usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3.
133                                 | wgpu::TextureUsages::TEXTURE_BINDING,
134             view_formats: &[],
135         };
136         let depth_texture = device.create_texture(&depth_descriptor);
137
138         let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
139         let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
140             address_mode_u: wgpu::AddressMode::ClampToEdge,
141             address_mode_v: wgpu::AddressMode::ClampToEdge,
142             address_mode_w: wgpu::AddressMode::ClampToEdge,
143             mag_filter: wgpu::FilterMode::Linear,
144             min_filter: wgpu::FilterMode::Linear,
145             mipmap_filter: wgpu::FilterMode::Nearest,
146             compare: Some(wgpu::CompareFunction::LessEqual),
147             lod_min_clamp: 0.0,
148             lod_max_clamp: 100.0,
149             ..Default::default()
150         });
151
152         (depth_texture, depth_view, depth_sampler)
153     }
154
155     pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
156         if size.width > 0 && size.height > 0 {
157             self.config.width = size.width;
158             self.config.height = size.height;
159             self.configure_surface();
160             self.update_projection();
161             (self.depth_texture, self.depth_view, self.depth_sampler) =
162                 Self::create_depth_texture(&self.config, &self.device);
163         }
164     }
165
166     pub fn configure_surface(&mut self) {
167         self.surface.configure(&self.device, &self.config);
168     }
169
170     pub fn update_projection(&mut self) {
171         self.proj = cgmath::perspective(
172             self.fov,
173             self.config.width as f32 / self.config.height as f32,
174             0.1,
175             100000.0,
176         );
177     }
178
179     pub fn update(&mut self, dt: Duration) {
180         let cam = self.camera.camera(dt.as_secs_f32());
181         self.camera.position = cam.position;
182         self.view = Matrix4::from(cam.orthogonal());
183
184         self.camera_uniform.set(&self.queue, self.proj * self.view);
185     }
186
187     pub fn render(&self, map: &Option<super::map::MapRender>) -> Result<(), wgpu::SurfaceError> {
188         let output = self.surface.get_current_texture()?;
189         let view = output
190             .texture
191             .create_view(&wgpu::TextureViewDescriptor::default());
192
193         let mut encoder = self
194             .device
195             .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
196
197         {
198             let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
199                 label: None,
200                 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
201                     view: &view,
202                     resolve_target: None,
203                     ops: wgpu::Operations {
204                         load: wgpu::LoadOp::Clear(wgpu::Color {
205                             r: 0x87 as f64 / 255.0,
206                             g: 0xCE as f64 / 255.0,
207                             b: 0xEB as f64 / 255.0,
208                             a: 1.0,
209                         }),
210                         store: true,
211                     },
212                 })],
213                 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
214                     view: &self.depth_view,
215                     depth_ops: Some(wgpu::Operations {
216                         load: wgpu::LoadOp::Clear(1.0),
217                         store: true,
218                     }),
219                     stencil_ops: None,
220                 }),
221             });
222
223             if let Some(map) = map.as_ref() {
224                 map.render(self, &mut render_pass);
225             }
226         }
227
228         self.queue.submit(std::iter::once(encoder.finish()));
229         output.present();
230
231         Ok(())
232     }
233 }