]> git.lizzy.rs Git - mt_client.git/blob - src/gfx/map.rs
Param1 light and allfaces
[mt_client.git] / src / gfx / map.rs
1 use super::{media::MediaMgr, state::State, util::MatrixUniform};
2 use cgmath::{prelude::*, Matrix4, Point3, Vector3};
3 use mt_net::{MapBlock, NodeDef};
4 use rand::Rng;
5 use std::{collections::HashMap, ops::Range};
6 use wgpu::util::DeviceExt;
7
8 pub struct MapRender {
9     pipeline: wgpu::RenderPipeline,
10     textures: HashMap<String, [Range<f32>; 2]>,
11     nodes: HashMap<u16, NodeDef>,
12     atlas: wgpu::BindGroup,
13     model: wgpu::BindGroupLayout,
14     blocks: HashMap<[i16; 3], BlockMesh>,
15 }
16
17 #[repr(C)]
18 #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
19 struct Vertex {
20     pos: [f32; 3],
21     tex_coords: [f32; 2],
22     light: f32,
23 }
24
25 impl Vertex {
26     const ATTRIBS: [wgpu::VertexAttribute; 3] =
27         wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32];
28
29     fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
30         wgpu::VertexBufferLayout {
31             array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
32             step_mode: wgpu::VertexStepMode::Vertex,
33             attributes: &Self::ATTRIBS,
34         }
35     }
36 }
37
38 struct BlockMesh {
39     vertex_buffer: wgpu::Buffer,
40     num_vertices: u32,
41     model: MatrixUniform,
42 }
43
44 impl MapRender {
45     pub fn render<'a>(&'a self, state: &'a State, pass: &mut wgpu::RenderPass<'a>) {
46         pass.set_pipeline(&self.pipeline);
47         pass.set_bind_group(0, &self.atlas, &[]);
48         pass.set_bind_group(1, &state.camera_uniform.bind_group, &[]);
49
50         for mesh in self.blocks.values() {
51             pass.set_bind_group(2, &mesh.model.bind_group, &[]);
52             pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
53             pass.draw(0..mesh.num_vertices, 0..1);
54         }
55     }
56
57     pub fn add_block(&mut self, state: &mut State, pos: Point3<i16>, block: Box<MapBlock>) {
58         let mut vertices = Vec::with_capacity(10000);
59         for (index, content) in block.param_0.iter().enumerate() {
60             let def = match self.nodes.get(content) {
61                 Some(x) => x,
62                 None => continue,
63             };
64
65             use lerp::Lerp;
66             use mt_net::{DrawType, Param1Type};
67             use std::array::from_fn as array;
68
69             match def.draw_type {
70                 DrawType::Cube | DrawType::AllFaces | DrawType::AllFacesOpt => {
71                     let light = match def.param1_type {
72                         Param1Type::Light => {
73                             println!("{}", block.param_1[index]);
74
75                             block.param_1[index] as f32 / 15.0
76                         }
77                         _ => 1.0,
78                     };
79
80                     let pos: [i16; 3] = array(|i| ((index >> (4 * i)) & 0xf) as i16);
81                     for (f, face) in CUBE.iter().enumerate() {
82                         let dir = FACE_DIR[f];
83                         let npos: [i16; 3] = array(|i| dir[i] + pos[i]);
84                         if npos.iter().all(|x| (0..16).contains(x)) {
85                             let nindex = npos[0] | (npos[1] << 4) | (npos[2] << 8);
86
87                             if let Some(ndef) = self.nodes.get(&block.param_0[nindex as usize]) {
88                                 if ndef.draw_type == DrawType::Cube {
89                                     continue;
90                                 }
91                             }
92                         }
93
94                         let tile = &def.tiles[f];
95                         let rect = self.textures.get(&tile.texture).unwrap();
96
97                         for vertex in face.iter() {
98                             /*println!(
99                                 "{:?} {:?} {:?} {:?}",
100                                 (vertex.1[0], vertex.1[1]),
101                                 (rect[0].start, rect[1].start),
102                                 (rect[0].end, rect[1].end),
103                                 (
104                                     vertex.1[0].lerp(rect[0].start, rect[0].end),
105                                     vertex.1[1].lerp(rect[1].start, rect[1].end)
106                                 )
107                             );*/
108                             vertices.push(Vertex {
109                                 pos: array(|i| pos[i] as f32 - 8.5 + vertex.0[i]),
110                                 tex_coords: array(|i| rect[i].start.lerp(rect[i].end, vertex.1[i])),
111                                 light,
112                             })
113                         }
114                     }
115                 }
116                 DrawType::None => {}
117                 _ => {
118                     // TODO
119                 }
120             }
121         }
122
123         self.blocks.insert(
124             pos.into(),
125             BlockMesh {
126                 vertex_buffer: state
127                     .device
128                     .create_buffer_init(&wgpu::util::BufferInitDescriptor {
129                         label: Some("mapblock.vertex_buffer"),
130                         contents: bytemuck::cast_slice(&vertices),
131                         usage: wgpu::BufferUsages::VERTEX,
132                     }),
133                 num_vertices: vertices.len() as u32,
134                 model: MatrixUniform::new(
135                     &state.device,
136                     &self.model,
137                     Matrix4::from_translation(
138                         pos.cast::<f32>().unwrap().to_vec() * 16.0 + Vector3::new(8.5, 8.5, 8.5),
139                     ),
140                     "mapblock",
141                     false,
142                 ),
143             },
144         );
145     }
146
147     pub fn new(state: &mut State, media: &MediaMgr, nodes: HashMap<u16, NodeDef>) -> Self {
148         let mut rng = rand::thread_rng();
149         let mut atlas_map = HashMap::new();
150         let mut atlas_alloc = guillotiere::SimpleAtlasAllocator::new(guillotiere::size2(1, 1));
151
152         for node in nodes.values() {
153             let tiles = node
154                 .tiles
155                 .iter()
156                 .chain(node.overlay_tiles.iter())
157                 .chain(node.special_tiles.iter());
158
159             let load_texture = |texture: &str| {
160                 let payload = media
161                     .get(texture)
162                     .ok_or_else(|| format!("texture not found: {texture}"))?;
163
164                 image::load_from_memory(payload)
165                     .or_else(|_| {
166                         image::load_from_memory_with_format(payload, image::ImageFormat::Tga)
167                     })
168                     .map_err(|e| format!("failed to load texture {texture}: {e}"))
169                     .map(|x| image::imageops::flip_vertical(&x))
170             };
171
172             let mut make_texture = |texture: &str| {
173                 texture
174                     .split('^')
175                     .map(|part| match load_texture(part) {
176                         Ok(v) => v,
177                         Err(e) => {
178                             if !texture.is_empty() && !texture.contains('[') {
179                                 eprintln!("{e}");
180                             }
181
182                             let mut img = image::RgbImage::new(1, 1);
183                             rng.fill(&mut img.get_pixel_mut(0, 0).0);
184
185                             image::DynamicImage::from(img).to_rgba8()
186                         }
187                     })
188                     .reduce(|mut base, top| {
189                         image::imageops::overlay(&mut base, &top, 0, 0);
190                         base
191                     })
192                     .unwrap()
193             };
194
195             for tile in tiles {
196                 atlas_map.entry(tile.texture.clone()).or_insert_with(|| {
197                     let img = make_texture(&tile.texture);
198
199                     let dimensions = img.dimensions();
200                     let size = guillotiere::size2(dimensions.0 as i32, dimensions.1 as i32);
201
202                     loop {
203                         match atlas_alloc.allocate(size) {
204                             None => {
205                                 let mut atlas_size = atlas_alloc.size();
206                                 atlas_size.width *= 2;
207                                 atlas_size.height *= 2;
208                                 atlas_alloc.grow(atlas_size);
209                             }
210                             Some(v) => return (img, v),
211                         }
212                     }
213                 });
214             }
215         }
216
217         let atlas_size = atlas_alloc.size();
218         let mut atlas = image::RgbaImage::new(atlas_size.width as u32, atlas_size.height as u32);
219
220         let textures = atlas_map
221             .into_iter()
222             .map(|(name, (img, rect))| {
223                 let w = atlas_size.width as f32;
224                 let h = atlas_size.height as f32;
225
226                 let x = (rect.min.x as f32 / w)..(rect.max.x as f32 / w);
227                 let y = (rect.min.y as f32 / h)..(rect.max.y as f32 / h);
228
229                 use image::GenericImage;
230                 atlas
231                     .copy_from(&img, rect.min.x as u32, rect.min.y as u32)
232                     .unwrap();
233
234                 (name, [x, y])
235             })
236             .collect();
237
238         let size = wgpu::Extent3d {
239             width: atlas_size.width as u32,
240             height: atlas_size.height as u32,
241             depth_or_array_layers: 1,
242         };
243
244         let atlas_texture = state.device.create_texture(&wgpu::TextureDescriptor {
245             size,
246             mip_level_count: 1,
247             sample_count: 1,
248             dimension: wgpu::TextureDimension::D2,
249             format: wgpu::TextureFormat::Rgba8UnormSrgb,
250             usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
251             label: Some("tile_atlas"),
252             view_formats: &[],
253         });
254
255         state.queue.write_texture(
256             wgpu::ImageCopyTexture {
257                 texture: &atlas_texture,
258                 mip_level: 0,
259                 origin: wgpu::Origin3d::ZERO,
260                 aspect: wgpu::TextureAspect::All,
261             },
262             &atlas,
263             wgpu::ImageDataLayout {
264                 offset: 0,
265                 bytes_per_row: std::num::NonZeroU32::new(4 * atlas_size.width as u32),
266                 rows_per_image: std::num::NonZeroU32::new(atlas_size.height as u32),
267             },
268             size,
269         );
270
271         let atlas_view = atlas_texture.create_view(&wgpu::TextureViewDescriptor::default());
272
273         let atlas_sampler = state.device.create_sampler(&wgpu::SamplerDescriptor {
274             address_mode_u: wgpu::AddressMode::ClampToEdge,
275             address_mode_v: wgpu::AddressMode::ClampToEdge,
276             address_mode_w: wgpu::AddressMode::ClampToEdge,
277             // "We've got you surrounded, stop using Nearest filter"
278             // - "I hate bilinear filtering I hate bilinear filtering I hate bilinear filtering"
279             mag_filter: wgpu::FilterMode::Nearest,
280             min_filter: wgpu::FilterMode::Nearest,
281             mipmap_filter: wgpu::FilterMode::Nearest,
282             ..Default::default()
283         });
284
285         let atlas_bind_group_layout =
286             state
287                 .device
288                 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
289                     entries: &[
290                         wgpu::BindGroupLayoutEntry {
291                             binding: 0,
292                             visibility: wgpu::ShaderStages::FRAGMENT,
293                             ty: wgpu::BindingType::Texture {
294                                 multisampled: false,
295                                 view_dimension: wgpu::TextureViewDimension::D2,
296                                 sample_type: wgpu::TextureSampleType::Float { filterable: true },
297                             },
298                             count: None,
299                         },
300                         wgpu::BindGroupLayoutEntry {
301                             binding: 1,
302                             visibility: wgpu::ShaderStages::FRAGMENT,
303                             ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
304                             count: None,
305                         },
306                     ],
307                     label: Some("atlas.bind_group_layout"),
308                 });
309
310         let atlas_bind_group = state.device.create_bind_group(&wgpu::BindGroupDescriptor {
311             layout: &atlas_bind_group_layout,
312             entries: &[
313                 wgpu::BindGroupEntry {
314                     binding: 0,
315                     resource: wgpu::BindingResource::TextureView(&atlas_view),
316                 },
317                 wgpu::BindGroupEntry {
318                     binding: 1,
319                     resource: wgpu::BindingResource::Sampler(&atlas_sampler),
320                 },
321             ],
322             label: Some("atlas.bind_group"),
323         });
324
325         let model_bind_group_layout = MatrixUniform::layout(&state.device, "mapblock");
326
327         let shader = state
328             .device
329             .create_shader_module(wgpu::include_wgsl!("../../assets/shaders/map.wgsl"));
330
331         let pipeline_layout =
332             state
333                 .device
334                 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
335                     label: None,
336                     bind_group_layouts: &[
337                         &atlas_bind_group_layout,
338                         &model_bind_group_layout,
339                         &state.camera_bind_group_layout,
340                     ],
341                     push_constant_ranges: &[],
342                 });
343
344         let pipeline = state
345             .device
346             .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
347                 label: None,
348                 layout: Some(&pipeline_layout),
349                 vertex: wgpu::VertexState {
350                     module: &shader,
351                     entry_point: "vs_main",
352                     buffers: &[Vertex::desc()],
353                 },
354                 fragment: Some(wgpu::FragmentState {
355                     module: &shader,
356                     entry_point: "fs_main",
357                     targets: &[Some(wgpu::ColorTargetState {
358                         format: state.config.format,
359                         blend: Some(wgpu::BlendState::REPLACE),
360                         write_mask: wgpu::ColorWrites::ALL,
361                     })],
362                 }),
363                 primitive: wgpu::PrimitiveState {
364                     topology: wgpu::PrimitiveTopology::TriangleList,
365                     strip_index_format: None,
366                     front_face: wgpu::FrontFace::Ccw,
367                     cull_mode: Some(wgpu::Face::Back),
368                     polygon_mode: wgpu::PolygonMode::Fill,
369                     unclipped_depth: false,
370                     conservative: false,
371                 },
372                 depth_stencil: Some(wgpu::DepthStencilState {
373                     format: wgpu::TextureFormat::Depth32Float,
374                     depth_write_enabled: true,
375                     depth_compare: wgpu::CompareFunction::Less,
376                     stencil: wgpu::StencilState::default(),
377                     bias: wgpu::DepthBiasState::default(),
378                 }),
379                 multisample: wgpu::MultisampleState {
380                     count: 1,
381                     mask: !0,
382                     alpha_to_coverage_enabled: false,
383                 },
384                 multiview: None,
385             });
386
387         Self {
388             pipeline,
389             nodes,
390             textures,
391             atlas: atlas_bind_group,
392             model: model_bind_group_layout,
393             blocks: HashMap::new(),
394         }
395     }
396 }
397
398 #[rustfmt::skip]
399 const CUBE: [[([f32; 3], [f32; 2]); 6]; 6] = [
400         [
401                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
402                 ([ 0.5,  0.5,  0.5], [ 1.0,  0.0]),
403                 ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
404                 ([ 0.5,  0.5,  0.5], [ 1.0,  0.0]),
405                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
406                 ([-0.5,  0.5,  0.5], [ 0.0,  0.0]),
407         ],
408         [
409                 ([-0.5, -0.5, -0.5], [ 0.0,  1.0]),
410                 ([ 0.5, -0.5, -0.5], [ 1.0,  1.0]),
411                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
412                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
413                 ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
414                 ([-0.5, -0.5, -0.5], [ 0.0,  1.0]),
415         ],
416         [
417                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
418                 ([ 0.5, -0.5, -0.5], [ 0.0,  0.0]),
419                 ([ 0.5,  0.5, -0.5], [ 0.0,  1.0]),
420                 ([ 0.5, -0.5, -0.5], [ 0.0,  0.0]),
421                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
422                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
423         ],
424         [
425                 ([-0.5,  0.5,  0.5], [ 1.0,  1.0]),
426                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
427                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
428                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
429                 ([-0.5, -0.5,  0.5], [ 1.0,  0.0]),
430                 ([-0.5,  0.5,  0.5], [ 1.0,  1.0]),
431         ],
432         [
433                 ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
434                 ([ 0.5, -0.5,  0.5], [ 1.0,  0.0]),
435                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
436                 ([ 0.5,  0.5,  0.5], [ 1.0,  1.0]),
437                 ([-0.5,  0.5,  0.5], [ 0.0,  1.0]),
438                 ([-0.5, -0.5,  0.5], [ 0.0,  0.0]),
439         ],
440         [
441                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
442                 ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
443                 ([ 0.5, -0.5, -0.5], [ 1.0,  0.0]),
444                 ([ 0.5,  0.5, -0.5], [ 1.0,  1.0]),
445                 ([-0.5, -0.5, -0.5], [ 0.0,  0.0]),
446                 ([-0.5,  0.5, -0.5], [ 0.0,  1.0]),
447         ],
448 ];
449
450 #[rustfmt::skip]
451 const FACE_DIR: [[i16; 3]; 6] = [
452         [ 0,  1,  0],
453         [ 0, -1,  0],
454         [ 1,  0,  0],
455         [-1,  0,  0],
456         [ 0,  0,  1],
457         [ 0,  0, -1],
458 ];