diff --git a/src/client/camera.rs b/src/client/camera.rs index acfb4fb..e128486 100644 --- a/src/client/camera.rs +++ b/src/client/camera.rs @@ -8,6 +8,7 @@ pub struct Zoom { exp: f32, level: i32, mult: FixedDec, + inv_mult: FixedDec, } #[derive(Clone)] @@ -41,12 +42,16 @@ impl Zoom { Self { exp: scale, level, - mult: mult(level, scale), + mult: zoom_mult(level, scale), + inv_mult: inv_zoom_mult(level, scale), } } pub fn mult(&self) -> &FixedDec { &self.mult } + pub fn inv_mult(&self) -> &FixedDec { + &self.inv_mult + } pub fn level(&self) -> i32 { self.level } @@ -67,10 +72,15 @@ impl AddAssign for Zoom { self.exp -= 1.0; self.level -= 1; } - self.mult = mult(self.level, self.exp); + self.mult = zoom_mult(self.level, self.exp); + self.inv_mult = inv_zoom_mult(self.level, self.exp); } } -pub fn mult(level: i32, exp: f32) -> FixedDec { +pub fn zoom_mult(level: i32, exp: f32) -> FixedDec { (FixedDec::from(1) >> level) * FixedDec::from(exp.exp2()) } + +pub fn inv_zoom_mult(level: i32, exp: f32) -> FixedDec { + (FixedDec::from(1) << level) * FixedDec::from(1.0 / exp.exp2()) +} diff --git a/src/client/handle_input.rs b/src/client/handle_input.rs index 3f71ff2..6ca16eb 100644 --- a/src/client/handle_input.rs +++ b/src/client/handle_input.rs @@ -28,5 +28,8 @@ impl Client<'_> { if input.pressed(K::KeyD) { camera.pos.x += &speed; } + if input.pressed(K::KeyQ) { + self.snapshot = true; + } } } diff --git a/src/client/mod.rs b/src/client/mod.rs index f55f143..8856972 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -23,6 +23,7 @@ pub struct Client<'a> { exit: bool, prev_update: Instant, renderer: Renderer<'a>, + snapshot: bool, } impl Client<'_> { @@ -40,6 +41,7 @@ impl Client<'_> { exit: false, prev_update: Instant::now(), renderer, + snapshot: false, } } @@ -60,7 +62,8 @@ impl Client<'_> { WindowEvent::CloseRequested => self.exit = true, WindowEvent::Resized(size) => self.renderer.resize(size), WindowEvent::RedrawRequested => { - self.renderer.render(&self.camera); + self.renderer.render(&self.camera, self.snapshot); + self.snapshot = false; self.window.request_redraw(); } WindowEvent::CursorLeft { .. } => { diff --git a/src/client/render/compute/layout.rs b/src/client/render/compute/layout.rs index 4190fed..1e18aa3 100644 --- a/src/client/render/compute/layout.rs +++ b/src/client/render/compute/layout.rs @@ -37,7 +37,7 @@ impl Layout { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8Unorm, - usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING, + usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_SRC, view_formats: &[], }; let output = Texture::init( diff --git a/src/client/render/compute/mod.rs b/src/client/render/compute/mod.rs index 1582ad0..7ccad9e 100644 --- a/src/client/render/compute/mod.rs +++ b/src/client/render/compute/mod.rs @@ -46,6 +46,7 @@ impl ComputePipeline { } } if len != self.old_len { + println!("len: {}", len); self.old_len = len; self.pipeline = self.pipeline(device, &Self::shader(device, len)); self.work.set(work_vec(size.x, size.y, len)); diff --git a/src/client/render/mod.rs b/src/client/render/mod.rs index 2adc02f..3d3b9b8 100644 --- a/src/client/render/mod.rs +++ b/src/client/render/mod.rs @@ -121,12 +121,11 @@ impl Renderer<'_> { } } - pub fn render(&mut self, camera: &Camera) { + pub fn render(&mut self, camera: &Camera, snapshot: bool) { + // this comes from the fact that I want (0, 2) and (30, 4) + // probably a much better formula, or better yet let the user select self.len = (camera.zoom.level() as f32 / 15.0 + 2.0).round() as usize; - println!("{}", self.len); - // let new = (camera.zoom.level() as f32 / 15.0 + 2.0).round() as usize; - // println!("{}", new); self.compute_pipeline.update( &self.device, @@ -136,12 +135,14 @@ impl Renderer<'_> { &self.size, self.len, ); - self.chunk_view.update(camera, &self.size); + self.chunk_view.update(camera, &self.size, snapshot); self.render_pipeline.update( &self.device, &mut self.encoder, &mut self.staging_belt, &self.chunk_view.render, + &self.compute_pipeline.output, + snapshot, ); let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device)); diff --git a/src/client/render/output/data.rs b/src/client/render/output/data.rs index cc85c05..622a987 100644 --- a/src/client/render/output/data.rs +++ b/src/client/render/output/data.rs @@ -10,13 +10,20 @@ pub struct WindowView { pub stretch: Vector2, pub pos: Vector2, pub rendered_chunks: Vector2, + pub snapshot: u32, } unsafe impl bytemuck::Pod for WindowView {} unsafe impl bytemuck::Zeroable for WindowView {} impl WindowView { - pub fn from_camera_size(camera: &Camera, size: &Vector2) -> Self { + pub fn from_camera_size( + camera: &Camera, + ss_cam: Option<&Camera>, + size: &Vector2, + snapshot: bool, + ) -> Self { + // TODO: most of this is useless and just preparation for chunked textures if I add them let visible_chunks = (size * 2 / CHUNK_WIDTH).add_scalar(1); let rendered_chunks = Vector2::new( visible_chunks.x.next_power_of_two(), @@ -36,10 +43,21 @@ impl WindowView { let stretch = size.cast::() * camera.zoom.rel_zoom() / (CHUNK_WIDTH as f32); + let (pos, stretch) = if let Some(ss_cam) = ss_cam { + let s = camera.zoom.mult() * ss_cam.zoom.inv_mult(); + ( + ((&camera.pos - &ss_cam.pos) * ss_cam.zoom.inv_mult().clone()).map(f32::from) * 2.0, + Vector2::from_element(f32::from(s)), + ) + } else { + (Vector2::default(), Vector2::default()) + }; + Self { pos, stretch, rendered_chunks, + snapshot: snapshot as u32, } } } diff --git a/src/client/render/output/layout.rs b/src/client/render/output/layout.rs index f23d310..20b33a2 100644 --- a/src/client/render/output/layout.rs +++ b/src/client/render/output/layout.rs @@ -13,6 +13,7 @@ pub struct Layout { format: wgpu::TextureFormat, pub view: Storage, pub chunks: ResizableTexture, + pub snapshot: Texture, } pub const LABEL: &str = file!(); @@ -46,6 +47,29 @@ impl Layout { }; let chunks = ResizableTexture::new(device, texture_desc, view_desc); + let desc = wgpu::TextureDescriptor { + label: Some("compute output"), + size: wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::STORAGE_BINDING + | wgpu::TextureUsages::TEXTURE_BINDING + | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }; + let snapshot = Texture::init( + device, + desc, + wgpu::TextureViewDescriptor::default(), + wgpu::SamplerDescriptor::default(), + ); + let render_bind_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ @@ -80,6 +104,22 @@ impl Layout { ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, + wgpu::BindGroupLayoutEntry { + binding: 4, + visibility: wgpu::ShaderStages::FRAGMENT | wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 5, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, ], label: Some(LABEL), }); @@ -97,6 +137,7 @@ impl Layout { render_bind_layout, render_pipeline_layout, format: config.format, + snapshot, } } @@ -108,6 +149,8 @@ impl Layout { self.chunks.view_entry(1), input.view_bind_group_entry(2), input.sampler_bind_group_entry(3), + self.snapshot.view_bind_group_entry(4), + self.snapshot.sampler_bind_group_entry(5), ], label: Some(LABEL), }) diff --git a/src/client/render/output/mod.rs b/src/client/render/output/mod.rs index 9d1bea6..961fa51 100644 --- a/src/client/render/output/mod.rs +++ b/src/client/render/output/mod.rs @@ -39,7 +39,31 @@ impl RenderPipeline { encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, view: &WindowView, + input: &Texture, + snapshot: bool, ) { + if snapshot { + let size = input.texture.size(); + if self.snapshot.texture.size() != size { + self.snapshot.resize(device, size); + } + encoder.copy_texture_to_texture( + wgpu::TexelCopyTextureInfoBase { + texture: &input.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyTextureInfoBase { + texture: &self.snapshot.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + size, + ); + self.bind_group = self.bind_group(device, input); + } self.view .update(device, encoder, belt, bytemuck::bytes_of(view)); } diff --git a/src/client/render/output/shader.wgsl b/src/client/render/output/shader.wgsl index 61de6b9..86192bb 100644 --- a/src/client/render/output/shader.wgsl +++ b/src/client/render/output/shader.wgsl @@ -2,6 +2,7 @@ struct View { stretch: vec2, pos: vec2, rendered_chunks: vec2, + snapshot: u32, } @group(0) @binding(0) @@ -13,11 +14,16 @@ var chunks: texture_2d_array; var tex: texture_2d; @group(0) @binding(3) var sam: sampler; +@group(0) @binding(4) +var ss_t: texture_2d; +@group(0) @binding(5) +var ss_s: sampler; struct VertexOutput { @builtin(position) vertex_pos: vec4, @location(0) world_pos: vec2, @location(1) tex_pos: vec2, + @location(2) ss_pos: vec2, }; @vertex @@ -43,6 +49,9 @@ fn vs_main( ); out.tex_pos = pos2; out.tex_pos.y = 1.0 - out.tex_pos.y; + let pos3 = vec2(pos.x, -pos.y); + out.ss_pos = pos3 * view.stretch + view.pos; + out.ss_pos = (out.ss_pos + 1.0) / 2.0; return out; } @@ -61,7 +70,14 @@ fn fs_main( // ); // let cposf = vec2(cposu); // return vec4(cposf / rcf, 0.0, 1.0); - return textureSample(tex, sam, in.tex_pos); + let cur = textureSample(tex, sam, in.tex_pos); + let snp_bounds = all(in.ss_pos >= vec2(0.0)) && all(in.ss_pos <= vec2(1.0)); + if all(cur.rgb == vec3(0.0)) && snp_bounds { + let snp = textureSample(ss_t, ss_s, in.ss_pos).rgb; + return vec4(snp * 0.3, 1.0); + } else { + return cur; + } } fn div_euclid(x: i32, y: i32) -> i32 { diff --git a/src/client/render/view.rs b/src/client/render/view.rs index b746ad1..f93e033 100644 --- a/src/client/render/view.rs +++ b/src/client/render/view.rs @@ -11,6 +11,7 @@ pub struct ChunkView { pub render: WindowView, pub chunk_queue: HashSet>, pub visible_chunks: HashSet>, + pub snapshot: Option, } impl ChunkView { @@ -18,8 +19,11 @@ impl ChunkView { Self::default() } - pub fn update(&mut self, camera: &Camera, size: &Vector2) { - let render = WindowView::from_camera_size(camera, size); + pub fn update(&mut self, camera: &Camera, size: &Vector2, snapshot: bool) { + if snapshot { + self.snapshot = Some(camera.clone()); + } + let render = WindowView::from_camera_size(camera, self.snapshot.as_ref(), size, snapshot); if self.render == render { return; @@ -63,4 +67,3 @@ impl ChunkView { self.visible_chunks = visible; } } -