1 use super::{media::MediaMgr, state::State, util::MatrixUniform};
2 use cgmath::{prelude::*, Matrix4, Point3, Vector3};
3 use mt_net::{MapBlock, NodeDef};
5 use std::{collections::HashMap, ops::Range};
6 use wgpu::util::DeviceExt;
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>,
18 #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
26 const ATTRIBS: [wgpu::VertexAttribute; 3] =
27 wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32];
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,
39 vertex_buffer: wgpu::Buffer,
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, &[]);
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);
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) {
66 use mt_net::{DrawType, Param1Type};
67 use std::array::from_fn as array;
70 DrawType::Cube | DrawType::AllFaces | DrawType::AllFacesOpt => {
71 let light = match def.param1_type {
72 Param1Type::Light => {
73 println!("{}", block.param_1[index]);
75 block.param_1[index] as f32 / 15.0
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);
87 if let Some(ndef) = self.nodes.get(&block.param_0[nindex as usize]) {
88 if ndef.draw_type == DrawType::Cube {
94 let tile = &def.tiles[f];
95 let rect = self.textures.get(&tile.texture).unwrap();
97 for vertex in face.iter() {
99 "{:?} {:?} {:?} {:?}",
100 (vertex.1[0], vertex.1[1]),
101 (rect[0].start, rect[1].start),
102 (rect[0].end, rect[1].end),
104 vertex.1[0].lerp(rect[0].start, rect[0].end),
105 vertex.1[1].lerp(rect[1].start, rect[1].end)
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])),
128 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
129 label: Some("mapblock.vertex_buffer"),
130 contents: bytemuck::cast_slice(&vertices),
131 usage: wgpu::BufferUsages::VERTEX,
133 num_vertices: vertices.len() as u32,
134 model: MatrixUniform::new(
137 Matrix4::from_translation(
138 pos.cast::<f32>().unwrap().to_vec() * 16.0 + Vector3::new(8.5, 8.5, 8.5),
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));
152 for node in nodes.values() {
156 .chain(node.overlay_tiles.iter())
157 .chain(node.special_tiles.iter());
159 let load_texture = |texture: &str| {
162 .ok_or_else(|| format!("texture not found: {texture}"))?;
164 image::load_from_memory(payload)
166 image::load_from_memory_with_format(payload, image::ImageFormat::Tga)
168 .map_err(|e| format!("failed to load texture {texture}: {e}"))
169 .map(|x| image::imageops::flip_vertical(&x))
172 let mut make_texture = |texture: &str| {
175 .map(|part| match load_texture(part) {
178 if !texture.is_empty() && !texture.contains('[') {
182 let mut img = image::RgbImage::new(1, 1);
183 rng.fill(&mut img.get_pixel_mut(0, 0).0);
185 image::DynamicImage::from(img).to_rgba8()
188 .reduce(|mut base, top| {
189 image::imageops::overlay(&mut base, &top, 0, 0);
196 atlas_map.entry(tile.texture.clone()).or_insert_with(|| {
197 let img = make_texture(&tile.texture);
199 let dimensions = img.dimensions();
200 let size = guillotiere::size2(dimensions.0 as i32, dimensions.1 as i32);
203 match atlas_alloc.allocate(size) {
205 let mut atlas_size = atlas_alloc.size();
206 atlas_size.width *= 2;
207 atlas_size.height *= 2;
208 atlas_alloc.grow(atlas_size);
210 Some(v) => return (img, v),
217 let atlas_size = atlas_alloc.size();
218 let mut atlas = image::RgbaImage::new(atlas_size.width as u32, atlas_size.height as u32);
220 let textures = atlas_map
222 .map(|(name, (img, rect))| {
223 let w = atlas_size.width as f32;
224 let h = atlas_size.height as f32;
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);
229 use image::GenericImage;
231 .copy_from(&img, rect.min.x as u32, rect.min.y as u32)
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,
244 let atlas_texture = state.device.create_texture(&wgpu::TextureDescriptor {
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"),
255 state.queue.write_texture(
256 wgpu::ImageCopyTexture {
257 texture: &atlas_texture,
259 origin: wgpu::Origin3d::ZERO,
260 aspect: wgpu::TextureAspect::All,
263 wgpu::ImageDataLayout {
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),
271 let atlas_view = atlas_texture.create_view(&wgpu::TextureViewDescriptor::default());
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,
285 let atlas_bind_group_layout =
288 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
290 wgpu::BindGroupLayoutEntry {
292 visibility: wgpu::ShaderStages::FRAGMENT,
293 ty: wgpu::BindingType::Texture {
295 view_dimension: wgpu::TextureViewDimension::D2,
296 sample_type: wgpu::TextureSampleType::Float { filterable: true },
300 wgpu::BindGroupLayoutEntry {
302 visibility: wgpu::ShaderStages::FRAGMENT,
303 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
307 label: Some("atlas.bind_group_layout"),
310 let atlas_bind_group = state.device.create_bind_group(&wgpu::BindGroupDescriptor {
311 layout: &atlas_bind_group_layout,
313 wgpu::BindGroupEntry {
315 resource: wgpu::BindingResource::TextureView(&atlas_view),
317 wgpu::BindGroupEntry {
319 resource: wgpu::BindingResource::Sampler(&atlas_sampler),
322 label: Some("atlas.bind_group"),
325 let model_bind_group_layout = MatrixUniform::layout(&state.device, "mapblock");
329 .create_shader_module(wgpu::include_wgsl!("../../assets/shaders/map.wgsl"));
331 let pipeline_layout =
334 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
336 bind_group_layouts: &[
337 &atlas_bind_group_layout,
338 &model_bind_group_layout,
339 &state.camera_bind_group_layout,
341 push_constant_ranges: &[],
346 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
348 layout: Some(&pipeline_layout),
349 vertex: wgpu::VertexState {
351 entry_point: "vs_main",
352 buffers: &[Vertex::desc()],
354 fragment: Some(wgpu::FragmentState {
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,
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,
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(),
379 multisample: wgpu::MultisampleState {
382 alpha_to_coverage_enabled: false,
391 atlas: atlas_bind_group,
392 model: model_bind_group_layout,
393 blocks: HashMap::new(),
399 const CUBE: [[([f32; 3], [f32; 2]); 6]; 6] = [
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]),
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]),
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]),
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]),
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]),
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]),
451 const FACE_DIR: [[i16; 3]; 6] = [