4 use super::{media::MediaMgr, state::State, util::MatrixUniform};
5 use atlas::create_atlas;
6 use cgmath::{prelude::*, Matrix4, Point3, Vector3};
8 use mt_net::{MapBlock, NodeDef};
9 use serde::{Deserialize, Serialize};
10 use std::{collections::HashMap, sync::Arc};
11 use wgpu::util::DeviceExt;
13 #[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug)]
14 #[serde(rename_all = "snake_case")]
21 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
22 pub struct MapRenderSettings {
23 pub leaves: LeavesMode,
24 pub opaque_liquids: bool,
27 impl Default for MapRenderSettings {
28 fn default() -> Self {
30 leaves: LeavesMode::Fancy,
31 opaque_liquids: false,
37 cube_tex_coords: [[[f32; 2]; 6]; 6],
41 // i optimized the shit out of these
42 textures: Vec<AtlasSlice>,
43 nodes: [Option<Box<NodeDef>>; u16::MAX as usize + 1],
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,
56 #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
64 const ATTRIBS: [wgpu::VertexAttribute; 3] =
65 wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32];
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,
77 vertex_buffer: wgpu::Buffer,
82 fn new(state: &State, vertices: &[Vertex]) -> Option<Self> {
83 if vertices.is_empty() {
90 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
91 label: Some("mapblock.vertex_buffer"),
92 contents: bytemuck::cast_slice(vertices),
93 usage: wgpu::BufferUsages::VERTEX,
95 num_vertices: vertices.len() as u32,
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);
107 mesh: Option<BlockMesh>,
108 mesh_blend: Option<BlockMesh>,
109 transform: MatrixUniform,
112 fn block_float_pos(pos: Vector3<i16>) -> Vector3<f32> {
113 pos.cast::<f32>().unwrap() * 16.0
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, &[]);
122 struct BlendEntry<'a> {
126 transform: &'a MatrixUniform,
129 let mut blend = Vec::new();
131 for (index, (pos, model)) in self.blocks.iter().enumerate() {
132 if let Some(mesh) = &model.mesh {
133 mesh.render(pass, &model.transform);
136 if let Some(mesh) = &model.mesh_blend {
137 blend.push(BlendEntry {
139 dist: (block_float_pos(Vector3::from(*pos))
140 - Vector3::from(state.camera.position))
143 transform: &model.transform,
148 blend.sort_unstable_by(|a, b| {
150 .partial_cmp(&b.dist)
151 .unwrap_or(std::cmp::Ordering::Equal)
152 .then_with(|| a.index.cmp(&b.index))
156 entry.mesh.render(pass, entry.transform);
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(),
172 mesh: BlockMesh::new(state, &data.vertices),
173 mesh_blend: BlockMesh::new(state, &data.vertices_blend),
174 transform: MatrixUniform::new(
177 Matrix4::from_translation(
178 block_float_pos(pos.to_vec()) + Vector3::new(8.5, 8.5, 8.5),
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);
190 let atlas_size = wgpu::Extent3d {
191 width: atlas_img.width(),
192 height: atlas_img.height(),
193 depth_or_array_layers: 1,
196 let atlas_texture = state.device.create_texture(&wgpu::TextureDescriptor {
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"),
207 state.queue.write_texture(
208 wgpu::ImageCopyTexture {
209 texture: &atlas_texture,
211 origin: wgpu::Origin3d::ZERO,
212 aspect: wgpu::TextureAspect::All,
215 wgpu::ImageDataLayout {
217 bytes_per_row: std::num::NonZeroU32::new(4 * atlas_img.width()),
218 rows_per_image: std::num::NonZeroU32::new(atlas_img.height()),
223 let atlas_view = atlas_texture.create_view(&wgpu::TextureViewDescriptor::default());
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,
237 let atlas_bind_group_layout =
240 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
242 wgpu::BindGroupLayoutEntry {
244 visibility: wgpu::ShaderStages::FRAGMENT,
245 ty: wgpu::BindingType::Texture {
247 view_dimension: wgpu::TextureViewDimension::D2,
248 sample_type: wgpu::TextureSampleType::Float { filterable: true },
252 wgpu::BindGroupLayoutEntry {
254 visibility: wgpu::ShaderStages::FRAGMENT,
255 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
259 label: Some("atlas.bind_group_layout"),
262 let atlas_bind_group = state.device.create_bind_group(&wgpu::BindGroupDescriptor {
263 layout: &atlas_bind_group_layout,
265 wgpu::BindGroupEntry {
267 resource: wgpu::BindingResource::TextureView(&atlas_view),
269 wgpu::BindGroupEntry {
271 resource: wgpu::BindingResource::Sampler(&atlas_sampler),
274 label: Some("atlas.bind_group"),
277 let model_bind_group_layout = MatrixUniform::layout(&state.device, "mapblock");
281 .create_shader_module(wgpu::include_wgsl!("../../assets/shaders/map.wgsl"));
283 let pipeline_layout =
286 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
288 bind_group_layouts: &[
289 &atlas_bind_group_layout,
290 &model_bind_group_layout,
291 &state.camera_bind_group_layout,
293 push_constant_ranges: &[],
298 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
300 layout: Some(&pipeline_layout),
301 vertex: wgpu::VertexState {
303 entry_point: "vs_main",
304 buffers: &[Vertex::desc()],
306 fragment: Some(wgpu::FragmentState {
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,
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,
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(),
331 multisample: wgpu::MultisampleState {
334 alpha_to_coverage_enabled: false,
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,
345 mesh_data_buffer: 16384,
346 atlas: atlas_bind_group,
347 model: model_bind_group_layout,
348 blocks: HashMap::new(),
354 const CUBE: [[([f32; 3], [f32; 2]); 6]; 6] = [
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]),
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]),
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]),
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]),
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]),
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]),