From f1a91b140498627075d4fe209e3aab551e007222 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Mon, 31 Mar 2025 22:51:48 -0400 Subject: [PATCH] mouse controls --- README.md | 6 ++-- src/client/camera.rs | 39 +++++++++++++++++++----- src/client/handle_input.rs | 36 +++++++++++++++++++--- src/client/input.rs | 9 +++--- src/client/mod.rs | 20 +++++++------ src/client/render/compute/data.rs | 16 ++++------ src/client/render/compute/mod.rs | 14 ++------- src/client/render/compute/shader.wgsl | 19 ++---------- src/client/render/mod.rs | 17 ++++++----- src/client/render/output/data.rs | 8 ++--- src/client/render/output/shader.wgsl | 29 ++++-------------- src/client/render/view.rs | 43 ++------------------------- src/main.rs | 1 + src/util/fixed/mod.rs | 6 ++++ 14 files changed, 121 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 038f115..84d94db 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ It's normal rust, so `cargo run` should fully compile and start it. It requires ## Controls - - WASD for movement + - WASD or left click & drag for movement - Scroll to zoom - - Q to take a snapshot + - Q or right click to take a snapshot Snapshots will copy the current texture and let you view it as the new one generates, which is very important for your sanity when you zoom in really far; the undecided regions will be replaced with a darkened version of your snapshot, so you can still know where you are and move around. @@ -48,8 +48,6 @@ not in order of priority - add auto snapshot; hard to figure out exactly when to take; maybe wait until at iter threshold dependent on zoom? - add ability to have multiple snapshots at once, so you can easily navigate around; also fade out snapshots that are far away zoom wise; also maybe save manual ones to disk so you can easily contiune exploring areas - add checkpointing that somehow lets you save & return to locations; even if this isn't added, add camera reset to easily get back to initial state; would also let you share locations with other people -- zoom in on mouse -- others controls for mouse; click & drag to move, maybe right click could be snapshot ## Cool Screenshots diff --git a/src/client/camera.rs b/src/client/camera.rs index b8f106c..8ae37e9 100644 --- a/src/client/camera.rs +++ b/src/client/camera.rs @@ -1,5 +1,5 @@ use nalgebra::Vector2; -use std::ops::AddAssign; +use std::ops::{AddAssign, Neg}; use crate::util::FixedDec; @@ -15,20 +15,44 @@ pub struct Zoom { pub struct Camera { pub pos: Vector2, pub zoom: Zoom, + pub size: Vector2, } impl Camera { - pub fn scale(&self, size: &Vector2) -> Vector2 { - let fsize: Vector2 = size.cast(); - if size.x < size.y { + pub fn world_pos(&self, screen_pos: Vector2) -> Vector2 { + let mut pos = screen_pos + .component_div(&self.size.cast()) + .add_scalar(-0.5) + .component_mul(&self.stretch()) + .map(FixedDec::from); + pos.y.negate(); + pos *= self.zoom.mult().clone(); + pos += &self.pos; + pos + } + + pub fn world_delta(&self, screen_delta: Vector2) -> Vector2 { + let mut pos = screen_delta + .component_div(&self.size.cast()) + .component_mul(&(self.stretch() * 1.5)) + .map(FixedDec::from); + pos.y.negate(); + pos *= self.zoom.mult().clone(); + pos + } + + pub fn stretch(&self) -> Vector2 { + let fsize: Vector2 = self.size.cast(); + if self.size.x < self.size.y { Vector2::new(fsize.x / fsize.y, 1.0) } else { Vector2::new(1.0, fsize.y / fsize.x) } } - pub fn inv_scale(&self, size: &Vector2) -> Vector2 { - let fsize: Vector2 = size.cast(); - if size.x < size.y { + + pub fn inv_stretch(&self) -> Vector2 { + let fsize: Vector2 = self.size.cast(); + if self.size.x < self.size.y { Vector2::new(fsize.y / fsize.x, 1.0) } else { Vector2::new(1.0, fsize.x / fsize.y) @@ -39,6 +63,7 @@ impl Camera { impl Default for Camera { fn default() -> Self { Self { + size: Vector2::zeros(), pos: Vector2::new(-0.5, 0.0).map(FixedDec::from), zoom: Zoom::new(0, 2.1), } diff --git a/src/client/handle_input.rs b/src/client/handle_input.rs index 6ca16eb..41155de 100644 --- a/src/client/handle_input.rs +++ b/src/client/handle_input.rs @@ -1,18 +1,46 @@ use std::time::Duration; -use winit::keyboard::KeyCode as K; +use winit::{event::MouseButton, keyboard::KeyCode as K}; use crate::util::FixedDec; use super::Client; +pub struct InputHandling { + pub snapshot: bool, +} + +impl InputHandling { + pub fn new() -> Self { + Self { snapshot: false } + } +} + impl Client<'_> { pub fn handle_input(&mut self, delta: Duration) { - let Client { input, camera, .. } = self; + let Client { + input, + camera, + handling, + .. + } = self; + if delta > Duration::from_secs_f32(0.5) { + // skip input handling if lag spike so you don't go flying + return; + } let per_sec = delta.as_secs_f32(); if input.scroll_delta != 0.0 { + let old_pos = camera.world_pos(input.mouse_pos); camera.zoom += input.scroll_delta / 5.0; + let new_pos = camera.world_pos(input.mouse_pos); + camera.pos += old_pos - new_pos; + } + + if input.mouse_pressed(MouseButton::Left) + && (input.mouse_delta.x != 0.0 || input.mouse_delta.y != 0.0) + { + camera.pos -= camera.world_delta(input.mouse_delta); } let speed = FixedDec::from(per_sec * 0.5) * camera.zoom.mult(); @@ -28,8 +56,8 @@ impl Client<'_> { if input.pressed(K::KeyD) { camera.pos.x += &speed; } - if input.pressed(K::KeyQ) { - self.snapshot = true; + if input.just_pressed(K::KeyQ) || input.mouse_just_pressed(MouseButton::Right) { + handling.snapshot = true; } } } diff --git a/src/client/input.rs b/src/client/input.rs index 2444de7..970963d 100644 --- a/src/client/input.rs +++ b/src/client/input.rs @@ -7,7 +7,7 @@ use winit::{ }; pub struct Input { - pub mouse_pixel_pos: Vector2, + pub mouse_pos: Vector2, pub mouse_delta: Vector2, pressed: HashSet, @@ -23,7 +23,7 @@ pub struct Input { impl Input { pub fn new() -> Self { Self { - mouse_pixel_pos: Vector2::zeros(), + mouse_pos: Vector2::zeros(), mouse_delta: Vector2::zeros(), pressed: HashSet::new(), just_pressed: HashSet::new(), @@ -67,11 +67,10 @@ impl Input { }; } WindowEvent::CursorLeft { .. } => { - self.pressed.clear(); - self.mouse_pressed.clear(); + self.clear(); } WindowEvent::CursorMoved { position, .. } => { - self.mouse_pixel_pos = Vector2::new(position.x as f32, position.y as f32); + self.mouse_pos = Vector2::new(position.x as f32, position.y as f32); } WindowEvent::MouseInput { button, state, .. } => match state { ElementState::Pressed => { diff --git a/src/client/mod.rs b/src/client/mod.rs index 8856972..4896750 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,6 +1,7 @@ use std::{sync::Arc, time::Instant}; use camera::Camera; +use handle_input::InputHandling; use input::Input; use render::Renderer; use winit::{ @@ -23,7 +24,7 @@ pub struct Client<'a> { exit: bool, prev_update: Instant, renderer: Renderer<'a>, - snapshot: bool, + handling: InputHandling, } impl Client<'_> { @@ -41,7 +42,7 @@ impl Client<'_> { exit: false, prev_update: Instant::now(), renderer, - snapshot: false, + handling: InputHandling::new(), } } @@ -60,16 +61,17 @@ impl Client<'_> { pub fn window_event(&mut self, event: WindowEvent) { match event { WindowEvent::CloseRequested => self.exit = true, - WindowEvent::Resized(size) => self.renderer.resize(size), + WindowEvent::Resized(size) => { + self.renderer.resize(size); + self.camera.size = *self.renderer.size(); + } WindowEvent::RedrawRequested => { - self.renderer.render(&self.camera, self.snapshot); - self.snapshot = false; + self.renderer.render(&self.camera, self.handling.snapshot); + self.handling.snapshot = false; self.window.request_redraw(); } - WindowEvent::CursorLeft { .. } => { - self.input.clear(); - } - _ => self.input.update_window(event), + _ => (), } + self.input.update_window(event); } } diff --git a/src/client/render/compute/data.rs b/src/client/render/compute/data.rs index 025657f..2581a35 100644 --- a/src/client/render/compute/data.rs +++ b/src/client/render/compute/data.rs @@ -19,7 +19,7 @@ impl ComputeView { impl Default for ComputeView { fn default() -> Self { let val = FixedDec::from_parts(false, 0, vec![0, 0, 0]); - Self::new(true, Vector2::zeros(), 0, &val, &val, &val) + Self::new(true, Vector2::zeros(), Vector2::zeros(), 0, &val, &val, &val) } } @@ -27,6 +27,7 @@ impl ComputeView { fn new( reset: bool, dims: Vector2, + stretch: Vector2, level: i32, scale: &FixedDec, x: &FixedDec, @@ -36,6 +37,7 @@ impl ComputeView { bytes.extend((reset as u32).to_le_bytes()); bytes.extend(level.to_le_bytes()); bytes.extend(bytemuck::cast_slice(&[dims.x, dims.y])); + bytes.extend(bytemuck::cast_slice(&[stretch.x, stretch.y])); scale.to_bytes(&mut bytes); x.to_bytes(&mut bytes); y.to_bytes(&mut bytes); @@ -46,7 +48,7 @@ impl ComputeView { Self { bytes } } - pub fn from_camera_size(camera: &Camera, size: &Vector2, reset: bool, len: usize) -> Self { + pub fn from_camera(camera: &Camera, reset: bool, len: usize) -> Self { let mut x = camera.pos.x.clone(); x.set_whole_len(1); x.set_dec_len(len as i32 - 1); @@ -54,17 +56,11 @@ impl ComputeView { y.set_whole_len(1); y.set_dec_len(len as i32 - 1); - let fsize: Vector2 = size.cast(); - let stretch = if size.x < size.y { - Vector2::new(fsize.x / fsize.y, 1.0) - } else { - Vector2::new(1.0, fsize.y / fsize.x) - }; - + let stretch = camera.stretch(); let mut scale = camera.zoom.mult().clone(); scale.set_precision(len); - Self::new(reset, *size, camera.zoom.level(), &scale, &x, &y) + Self::new(reset, camera.size, stretch, camera.zoom.level(), &scale, &x, &y) } } diff --git a/src/client/render/compute/mod.rs b/src/client/render/compute/mod.rs index 777cf72..3ed4a64 100644 --- a/src/client/render/compute/mod.rs +++ b/src/client/render/compute/mod.rs @@ -36,10 +36,9 @@ impl ComputePipeline { encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, camera: &Camera, - size: &Vector2, len: usize, ) { - let mut view = ComputeView::from_camera_size(camera, size, false, len); + let mut view = ComputeView::from_camera(camera, false, len); if view != self.old_view { for (i, b) in 1u32.to_le_bytes().iter().enumerate() { view.bytes[i] = *b; @@ -49,7 +48,7 @@ impl ComputePipeline { println!("new 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)); + self.work.set(work_vec(camera.size.x, camera.size.y, len)); } let updated = self.work.update(device, encoder, belt) | self.view.update(device, encoder, belt, view.bytes()); @@ -66,14 +65,7 @@ impl ComputePipeline { pass.dispatch_workgroups(240, 135, 1); } - pub fn resize( - &mut self, - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - belt: &mut wgpu::util::StagingBelt, - size: Vector2, - len: usize, - ) { + pub fn resize(&mut self, device: &wgpu::Device, size: Vector2, len: usize) { self.work.set(work_vec(size.x, size.y, len)); self.old_len = len; self.output.resize( diff --git a/src/client/render/compute/shader.wgsl b/src/client/render/compute/shader.wgsl index 98dbfa4..90beccd 100644 --- a/src/client/render/compute/shader.wgsl +++ b/src/client/render/compute/shader.wgsl @@ -9,6 +9,7 @@ struct View { reset: u32, level: i32, dims: vec2, + stretch: vec2, scale: FixedDec, corner_x: FixedDec, corner_y: FixedDec, @@ -28,30 +29,14 @@ fn main( if id.x > view.dims.x - 1 || id.y > view.dims.y - 1 { return; } - // TODO: actually use width let varwidth = LEN + 2; let workwidth = varwidth * 2 + 1; let worki = (id.x * view.dims.y + id.y) * workwidth; let xidx = worki + 1; let yidx = xidx + varwidth; - // let dec = view.corner_x.dec; - // var rel_x = FixedDec(POS, dec, array()); - // rel_x.parts[0] = id.x; - // rel_x = shr(rel_x, view.level); - // var rel_y = FixedDec(POS, dec, array()); - // rel_y.parts[0] = id.y; - // rel_y = shr(rel_y, view.level); - // let cx = add(view.corner_x, rel_x); - // let cy = add(view.corner_y, rel_y); let fdims = vec2(view.dims); - var stretch: vec2; - if fdims.x < fdims.y { - stretch = vec2(fdims.x / fdims.y, 1.0); - } else { - stretch = vec2(1.0, fdims.y / fdims.x); - } - let fpos = (vec2(id.xy) / fdims - 0.5) * stretch; + let fpos = (vec2(id.xy) / fdims - 0.5) * view.stretch; let cx = add(mul(from_f32(fpos.x), view.scale), view.corner_x); let cy = add(mul(from_f32(fpos.y), view.scale), view.corner_y); var x: FixedDec; diff --git a/src/client/render/mod.rs b/src/client/render/mod.rs index c20783b..72f6875 100644 --- a/src/client/render/mod.rs +++ b/src/client/render/mod.rs @@ -122,17 +122,14 @@ impl Renderer<'_> { } 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 / 32.0 + 2.0).round() as usize; + // at level 0 I want 2, and should increase respective to bits needed for positioning + self.len = (camera.zoom.level() / 32) as usize + 2; self.compute_pipeline.update( &self.device, &mut self.encoder, &mut self.staging_belt, camera, - &self.size, self.len, ); self.chunk_view.update(camera, &self.size, snapshot); @@ -168,8 +165,10 @@ impl Renderer<'_> { self.config.width = size.width; self.config.height = size.height; self.surface.configure(&self.device, &self.config); - self.compute_pipeline.resize(&self.device, &mut self.encoder, &mut self.staging_belt, self.size, self.len); - self.render_pipeline.resize(&self.device, self.size, &self.compute_pipeline.output); + self.compute_pipeline + .resize(&self.device, self.size, self.len); + self.render_pipeline + .resize(&self.device, self.size, &self.compute_pipeline.output); } fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder { @@ -177,4 +176,8 @@ impl Renderer<'_> { label: Some("Render Encoder"), }) } + + pub fn size(&self) -> &Vector2 { + &self.size + } } diff --git a/src/client/render/output/data.rs b/src/client/render/output/data.rs index a599726..e1deebd 100644 --- a/src/client/render/output/data.rs +++ b/src/client/render/output/data.rs @@ -1,7 +1,5 @@ use nalgebra::Vector2; -use crate::client::render::CHUNK_POW; - use super::{Camera, CHUNK_WIDTH}; #[repr(C, align(8))] @@ -44,10 +42,12 @@ 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 aspect = camera.inv_scale(size) * 2.0; + let aspect = camera.inv_stretch() * 2.0; let s = camera.zoom.mult() * ss_cam.zoom.inv_mult(); ( - ((&camera.pos - &ss_cam.pos) * ss_cam.zoom.inv_mult().clone()).map(f32::from).component_mul(&aspect), + ((&camera.pos - &ss_cam.pos) * ss_cam.zoom.inv_mult().clone()) + .map(f32::from) + .component_mul(&aspect), Vector2::from_element(f32::from(s)), ) } else { diff --git a/src/client/render/output/shader.wgsl b/src/client/render/output/shader.wgsl index 86192bb..9f75134 100644 --- a/src/client/render/output/shader.wgsl +++ b/src/client/render/output/shader.wgsl @@ -21,9 +21,8 @@ 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, + @location(0) tex_pos: vec2, + @location(1) ss_pos: vec2, }; @vertex @@ -35,22 +34,16 @@ fn vs_main( let pos = vec2( f32(vi % 2u), - f32(vi / 2u), + f32(1 - vi / 2u), ) * 2.0 - 1.0; - out.vertex_pos = vec4(pos.x, -pos.y, 0.0, 1.0); - out.world_pos = pos / 2.0; - out.world_pos.y *= -1.0; - out.world_pos *= view.stretch; - out.world_pos += view.pos; + out.vertex_pos = vec4(pos.x, pos.y, 0.0, 1.0); let pos2 = vec2( f32(vi % 2u), - f32(vi / 2u), + f32(1 - vi / 2u), ); 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 = pos * view.stretch + view.pos; out.ss_pos = (out.ss_pos + 1.0) / 2.0; return out; @@ -60,16 +53,6 @@ fn vs_main( fn fs_main( in: VertexOutput, ) -> @location(0) vec4 { - // let a = textureLoad(chunks, vec2(0), 0, 0); - // let rc = vec2(view.rendered_chunks); - // let rcf = vec2(rc); - // let cposi = vec2(floor(in.world_pos)); - // let cposu = vec2( - // rem_euclid(cposi.x, rc.x), - // rem_euclid(cposi.y, rc.y) - // ); - // let cposf = vec2(cposu); - // return vec4(cposf / rcf, 0.0, 1.0); 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 { diff --git a/src/client/render/view.rs b/src/client/render/view.rs index c47f217..c09dc83 100644 --- a/src/client/render/view.rs +++ b/src/client/render/view.rs @@ -2,15 +2,13 @@ use std::collections::HashSet; use nalgebra::Vector2; -use crate::{client::camera::Camera, util::FixedDec}; +use crate::client::camera::Camera; -use super::{output::WindowView, CHUNK_POW}; +use super::output::WindowView; #[derive(Default)] pub struct ChunkView { pub render: WindowView, - pub chunk_queue: HashSet>, - pub visible_chunks: HashSet>, pub snapshot: Option, } @@ -29,42 +27,5 @@ impl ChunkView { return; } self.render = render; - - let corner_offset = ((size / 2).cast() * camera.zoom.rel_zoom()) - .map(|x| FixedDec::from(x) >> camera.zoom.level()); - let bot_left = &camera.pos - &corner_offset; - let top_right = &camera.pos + &corner_offset; - let mult = FixedDec::one() >> (CHUNK_POW as i32 - camera.zoom.level()); - let blc = bot_left - .component_mul(&Vector2::from_element(mult.clone())) - .map(FixedDec::floor); - let trc = top_right - .component_mul(&Vector2::from_element(mult)) - .map(FixedDec::floor); - - let mut visible = HashSet::new(); - let mut x = blc.x.clone(); - // while x <= trc.x { - // let mut y = blc.y.clone(); - // while y <= trc.y { - // visible.insert(Vector2::new(x.clone(), y.clone())); - // y += FixedDec::one(); - // } - // x += FixedDec::one(); - // } - - - let new = visible - .difference(&self.visible_chunks) - .cloned() - .collect::>(); - let old = self - .visible_chunks - .difference(&visible) - .cloned() - .collect::>(); - self.chunk_queue.retain(|p| !old.contains(p)); - self.chunk_queue.extend(new); - self.visible_chunks = visible; } } diff --git a/src/main.rs b/src/main.rs index 7276fdc..8b67947 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![feature(bigint_helper_methods)] #![feature(int_roundings)] +#![feature(let_chains)] use client::ClientApp; diff --git a/src/util/fixed/mod.rs b/src/util/fixed/mod.rs index 707669d..2a2941a 100644 --- a/src/util/fixed/mod.rs +++ b/src/util/fixed/mod.rs @@ -139,6 +139,12 @@ impl FixedDec { pub fn parts(&self) -> &[u32] { &self.parts } + + pub fn negate(&mut self) { + if !self.is_zero() { + self.sign = !self.sign; + } + } } impl Display for FixedDec {