]> git.lizzy.rs Git - mt_client.git/blob - src/gfx/map.rs
e5a42f3a11f67dd622a14e6e179b56ea7457b700
[mt_client.git] / src / gfx / map.rs
1 mod atlas;
2 mod mesh;
3
4 use super::{media::MediaMgr, state::State, util::MatrixUniform};
5 use atlas::create_atlas;
6 use cgmath::{prelude::*, Matrix4, Point3, Vector3};
7 use mesh::create_mesh;
8 use mt_net::{MapBlock, NodeDef};
9 use serde::{Deserialize, Serialize};
10 use std::{collections::HashMap, sync::Arc};
11 use wgpu::util::DeviceExt;
12
13 #[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug)]
14 #[serde(rename_all = "snake_case")]
15 pub enum LeavesMode {
16     Opaque,
17     Simple,
18     Fancy,
19 }
20
21 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
22 pub struct MapRenderSettings {
23     pub leaves: LeavesMode,
24     pub opaque_liquids: bool,
25 }
26
27 impl Default for MapRenderSettings {
28     fn default() -> Self {
29         Self {
30             leaves: LeavesMode::Fancy,
31             opaque_liquids: false,
32         }
33     }
34 }
35
36 struct AtlasSlice {
37     cube_tex_coords: [[[f32; 2]; 6]; 6],
38 }
39
40 struct MeshMakeInfo {
41     // i optimized the shit out of these
42     textures: Vec<AtlasSlice>,
43     nodes: [Option<Box<NodeDef>>; u16::MAX as usize + 1],
44 }
45
46 pub struct MapRender {
47     pipeline: wgpu::RenderPipeline,
48     atlas: wgpu::BindGroup,
49     model: wgpu::BindGroupLayout,
50     blocks: HashMap<[i16; 3], BlockModel>,
51     mesh_make_info: Arc<MeshMakeInfo>,
52     mesh_data_buffer: usize,
53 }
54
55 #[repr(C)]
56 #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
57 struct Vertex {
58     pos: [f32; 3],
59     tex_coords: [f32; 2],
60     light: f32,
61 }
62
63 impl Vertex {
64     const ATTRIBS: [wgpu::VertexAttribute; 3] =
65         wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32];
66
67     fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
68         wgpu::VertexBufferLayout {
69             array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
70             step_mode: wgpu::VertexStepMode::Vertex,
71             attributes: &Self::ATTRIBS,
72         }
73     }
74 }
75
76 struct BlockMesh {
77     vertex_buffer: wgpu::Buffer,
78     num_vertices: u32,
79 }
80
81 impl BlockMesh {
82     fn new(state: &State, vertices: &[Vertex]) -> Option<Self> {
83         if vertices.is_empty() {
84             return None;
85         }
86
87         Some(BlockMesh {
88             vertex_buffer: state
89                 .device
90                 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
91                     label: Some("mapblock.vertex_buffer"),
92                     contents: bytemuck::cast_slice(vertices),
93                     usage: wgpu::BufferUsages::VERTEX,
94                 }),
95             num_vertices: vertices.len() as u32,
96         })
97     }
98
99     fn render<'a>(&'a self, pass: &mut wgpu::RenderPass<'a>, transform: &'a MatrixUniform) {
100         pass.set_bind_group(2, &transform.bind_group, &[]);
101         pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
102         pass.draw(0..self.num_vertices, 0..1);
103     }
104 }
105
106 struct BlockModel {
107     mesh: Option<BlockMesh>,
108     mesh_blend: Option<BlockMesh>,
109     transform: MatrixUniform,
110 }
111
112 fn block_float_pos(pos: Vector3<i16>) -> Vector3<f32> {
113     pos.cast::<f32>().unwrap() * 16.0
114 }
115
116 impl MapRender {
117     pub fn render<'a>(&'a self, state: &'a State, pass: &mut wgpu::RenderPass<'a>) {
118         pass.set_pipeline(&self.pipeline);
119         pass.set_bind_group(0, &self.atlas, &[]);
120         pass.set_bind_group(1, &state.camera_uniform.bind_group, &[]);
121
122         struct BlendEntry<'a> {
123             dist: f32,
124             index: usize,
125             mesh: &'a BlockMesh,
126             transform: &'a MatrixUniform,
127         }
128
129         let mut blend = Vec::new();
130
131         for (index, (pos, model)) in self.blocks.iter().enumerate() {
132             if let Some(mesh) = &model.mesh {
133                 mesh.render(pass, &model.transform);
134             }
135
136             if let Some(mesh) = &model.mesh_blend {
137                 blend.push(BlendEntry {
138                     index,
139                     dist: (block_float_pos(Vector3::from(*pos))
140                         - Vector3::from(state.camera.position))
141                     .magnitude(),
142                     mesh,
143                     transform: &model.transform,
144                 });
145             }
146         }
147
148         blend.sort_unstable_by(|a, b| {
149             a.dist
150                 .partial_cmp(&b.dist)
151                 .unwrap_or(std::cmp::Ordering::Equal)
152                 .then_with(|| a.index.cmp(&b.index))
153         });
154
155         for entry in blend {
156             entry.mesh.render(pass, entry.transform);
157         }
158     }
159
160     pub fn add_block(&mut self, state: &mut State, pos: Point3<i16>, block: Box<MapBlock>) {
161         let (pos, data) = create_mesh(
162             &mut self.mesh_data_buffer,
163             self.mesh_make_info.clone(),
164             &Default::default(),
165             pos,
166             block,
167         );
168
169         self.blocks.insert(
170             pos.into(),
171             BlockModel {
172                 mesh: BlockMesh::new(state, &data.vertices),
173                 mesh_blend: BlockMesh::new(state, &data.vertices_blend),
174                 transform: MatrixUniform::new(
175                     &state.device,
176                     &self.model,
177                     Matrix4::from_translation(
178                         block_float_pos(pos.to_vec()) + Vector3::new(8.5, 8.5, 8.5),
179                     ),
180                     "mapblock",
181                     false,
182                 ),
183             },
184         );
185     }
186
187     pub fn new(state: &mut State, media: &MediaMgr, mut nodes: HashMap<u16, NodeDef>) -> Self {
188         let (atlas_img, atlas_slices) = create_atlas(&mut nodes, media);
189
190         let atlas_size = wgpu::Extent3d {
191             width: atlas_img.width(),
192             height: atlas_img.height(),
193             depth_or_array_layers: 1,
194         };
195
196         let atlas_texture = state.device.create_texture(&wgpu::TextureDescriptor {
197             size: atlas_size,
198             mip_level_count: 1,
199             sample_count: 1,
200             dimension: wgpu::TextureDimension::D2,
201             format: wgpu::TextureFormat::Rgba8UnormSrgb,
202             usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
203             label: Some("tile_atlas"),
204             view_formats: &[],
205         });
206
207         state.queue.write_texture(
208             wgpu::ImageCopyTexture {
209                 texture: &atlas_texture,
210                 mip_level: 0,
211                 origin: wgpu::Origin3d::ZERO,
212                 aspect: wgpu::TextureAspect::All,
213             },
214             &atlas_img,
215             wgpu::ImageDataLayout {
216                 offset: 0,
217                 bytes_per_row: std::num::NonZeroU32::new(4 * atlas_img.width()),
218                 rows_per_image: std::num::NonZeroU32::new(atlas_img.height()),
219             },
220             atlas_size,
221         );
222
223         let atlas_view = atlas_texture.create_view(&wgpu::TextureViewDescriptor::default());
224
225         let atlas_sampler = state.device.create_sampler(&wgpu::SamplerDescriptor {
226             address_mode_u: wgpu::AddressMode::ClampToEdge,
227             address_mode_v: wgpu::AddressMode::ClampToEdge,
228             address_mode_w: wgpu::AddressMode::ClampToEdge,
229             // "We've got you surrounded, stop using Nearest filter"
230             // - "I hate bilinear filtering I hate bilinear filtering I hate bilinear filtering"
231             mag_filter: wgpu::FilterMode::Nearest,
232             min_filter: wgpu::FilterMode::Nearest,
233             mipmap_filter: wgpu::FilterMode::Nearest,
234             ..Default::default()
235         });
236
237         let atlas_bind_group_layout =
238             state
239                 .device
240                 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
241                     entries: &[
242                         wgpu::BindGroupLayoutEntry {
243                             binding: 0,
244                             visibility: wgpu::ShaderStages::FRAGMENT,
245                             ty: wgpu::BindingType::Texture {
246                                 multisampled: false,
247                                 view_dimension: wgpu::TextureViewDimension::D2,
248                                 sample_type: wgpu::TextureSampleType::Float { filterable: true },
249                             },
250                             count: None,
251                         },
252                         wgpu::BindGroupLayoutEntry {
253                             binding: 1,
254                             visibility: wgpu::ShaderStages::FRAGMENT,
255                             ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
256                             count: None,
257                         },
258                     ],
259                     label: Some("atlas.bind_group_layout"),
260                 });
261
262         let atlas_bind_group = state.device.create_bind_group(&wgpu::BindGroupDescriptor {
263             layout: &atlas_bind_group_layout,
264             entries: &[
265                 wgpu::BindGroupEntry {
266                     binding: 0,
267                     resource: wgpu::BindingResource::TextureView(&atlas_view),
268                 },
269                 wgpu::BindGroupEntry {
270                     binding: 1,
271                     resource: wgpu::BindingResource::Sampler(&atlas_sampler),
272                 },
273             ],
274             label: Some("atlas.bind_group"),
275         });
276
277         let model_bind_group_layout = MatrixUniform::layout(&state.device, "mapblock");
278
279         let shader = state
280             .device
281             .create_shader_module(wgpu::include_wgsl!("../../assets/shaders/map.wgsl"));
282
283         let pipeline_layout =
284             state
285                 .device
286                 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
287                     label: None,
288                     bind_group_layouts: &[
289                         &atlas_bind_group_layout,
290                         &model_bind_group_layout,
291                         &state.camera_bind_group_layout,
292                     ],
293                     push_constant_ranges: &[],
294                 });
295
296         let pipeline = state
297             .device
298             .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
299                 label: None,
300                 layout: Some(&pipeline_layout),
301                 vertex: wgpu::VertexState {
302                     module: &shader,
303                     entry_point: "vs_main",
304                     buffers: &[Vertex::desc()],
305                 },
306                 fragment: Some(wgpu::FragmentState {
307                     module: &shader,
308                     entry_point: "fs_main",
309                     targets: &[Some(wgpu::ColorTargetState {
310                         format: state.config.format,
311                         blend: Some(wgpu::BlendState::REPLACE),
312                         write_mask: wgpu::ColorWrites::ALL,
313                     })],
314                 }),
315                 primitive: wgpu::PrimitiveState {
316                     topology: wgpu::PrimitiveTopology::TriangleList,
317                     strip_index_format: None,
318                     front_face: wgpu::FrontFace::Ccw,
319                     cull_mode: Some(wgpu::Face::Back),
320                     polygon_mode: wgpu::PolygonMode::Fill,
321                     unclipped_depth: false,
322                     conservative: false,
323                 },
324                 depth_stencil: Some(wgpu::DepthStencilState {
325                     format: wgpu::TextureFormat::Depth32Float,
326                     depth_write_enabled: true,
327                     depth_compare: wgpu::CompareFunction::Less,
328                     stencil: wgpu::StencilState::default(),
329                     bias: wgpu::DepthBiasState::default(),
330                 }),
331                 multisample: wgpu::MultisampleState {
332                     count: 1,
333                     mask: !0,
334                     alpha_to_coverage_enabled: false,
335                 },
336                 multiview: None,
337             });
338
339         Self {
340             pipeline,
341             mesh_make_info: Arc::new(MeshMakeInfo {
342                 nodes: std::array::from_fn(|i| nodes.get(&(i as u16)).cloned().map(Box::new)),
343                 textures: atlas_slices,
344             }),
345             mesh_data_buffer: 16384,
346             atlas: atlas_bind_group,
347             model: model_bind_group_layout,
348             blocks: HashMap::new(),
349         }
350     }
351 }
352
353 #[rustfmt::skip]
354 const CUBE: [[([f32; 3], [f32; 2]); 6]; 6] = [
355         [
356                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
357                 ([ 0.5,  0.5,  0.5], [ 1.0,  0.0]),
358                 ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
359                 ([ 0.5,  0.5,  0.5], [ 1.0,  0.0]),
360                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
361                 ([-0.5,  0.5,  0.5], [ 0.0,  0.0]),
362         ],
363         [
364                 ([-0.5, -0.5, -0.5], [ 0.0,  1.0]),
365                 ([ 0.5, -0.5, -0.5], [ 1.0,  1.0]),
366                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
367                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
368                 ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
369                 ([-0.5, -0.5, -0.5], [ 0.0,  1.0]),
370         ],
371         [
372                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
373                 ([ 0.5, -0.5, -0.5], [ 0.0,  0.0]),
374                 ([ 0.5,  0.5, -0.5], [ 0.0,  1.0]),
375                 ([ 0.5, -0.5, -0.5], [ 0.0,  0.0]),
376                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
377                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
378         ],
379         [
380                 ([-0.5,  0.5,  0.5], [ 1.0,  1.0]),
381                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
382                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
383                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
384                 ([-0.5, -0.5,  0.5], [ 1.0,  0.0]),
385                 ([-0.5,  0.5,  0.5], [ 1.0,  1.0]),
386         ],
387         [
388                 ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
389                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
390                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
391                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
392                 ([-0.5,  0.5,  0.5], [ 0.0,  1.0]),
393                 ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
394         ],
395         [
396                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
397                 ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
398                 ([ 0.5, -0.5, -0.5], [ 1.0,  0.0]),
399                 ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
400                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
401                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
402         ],
403 ];