diff --git a/src/main.rs b/src/main.rs index 7c885c2..302ce03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -188,7 +188,13 @@ impl App<'_> { self.renderer.render(self, &mut encoder, &view); self.egui.render(gc, &mut encoder, &view, |ctx| { - self.gui.ui(ctx, gc, &mut self.simulation, &mut self.fps); + self.gui.ui( + ctx, + gc, + &mut self.simulation, + &mut self.renderer, + &mut self.fps, + ); }); let snapshot = self diff --git a/src/renderer.rs b/src/renderer.rs index 0bf0baa..6b4da56 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,5 +1,3 @@ -use std::mem; - use anyhow::Result; use encase::{ShaderType, UniformBuffer}; use image::{GenericImageView, ImageBuffer, Rgba}; @@ -26,6 +24,7 @@ pub struct Renderer { context: Buffer, pub pan: Vector2, + pub zoom: f32, } #[derive(ShaderType, Default)] @@ -37,6 +36,9 @@ pub struct RenderContext { flags: u32, gain: f32, energy_gain: f32, + + pan: Vector2, + zoom: f32, } impl Renderer { @@ -78,9 +80,7 @@ impl Renderer { ty: BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: false, - min_binding_size: BufferSize::new( - dbg!(mem::size_of::()) as _ - ), + min_binding_size: BufferSize::new(48), }, count: None, }, @@ -138,6 +138,7 @@ impl Renderer { context, pan: Vector2::zeros(), + zoom: 1.0, } } @@ -177,6 +178,8 @@ impl Renderer { // TODO: move out of simulation gain: app.simulation.gain, energy_gain: app.simulation.energy_gain, + pan: self.pan, + zoom: self.zoom, }) .unwrap(); gc.queue diff --git a/src/shaders/render.wgsl b/src/shaders/render.wgsl index feafc26..c544372 100644 --- a/src/shaders/render.wgsl +++ b/src/shaders/render.wgsl @@ -11,7 +11,10 @@ struct Context { // 1 << 1: energy_view flags: u32, gain: f32, - energy_gain: f32 + energy_gain: f32, + + pan: vec2, + zoom: f32, } // VERTEX SHADER // @@ -19,7 +22,7 @@ struct Context { struct VertexOutput { @builtin(position) position: vec4, -}; +} @vertex fn vert( @@ -52,20 +55,16 @@ fn index(x: u32, y: u32, n: u32) -> u32 { @fragment fn frag(in: VertexOutput) -> @location(0) vec4 { - // ↓ Use vector operations - let x_offset = ctx.window.x / 2 - ctx.size.x / 2; - let y_offset = ctx.window.y / 2 - ctx.size.y / 2; - let x = i32(in.position.x) - i32(x_offset); - let y = i32(in.position.y) - i32(y_offset); + let pos = vec2((in.position.xy - ctx.pan) * ctx.zoom ); - if x == -1 || y == -1 || x == i32(ctx.size.x) || y == i32(ctx.size.y) { + if pos.x == -1 || pos.y == -1 || pos.x == i32(ctx.size.x) || pos.y == i32(ctx.size.y) { return vec4(0.0, 0.0, 0.0, 1.0); - } else if x < 0 || x > i32(ctx.size.x) || y < 0 || y > i32(ctx.size.y) { + } else if pos.x < 0 || pos.x > i32(ctx.size.x) || pos.y < 0 || pos.y > i32(ctx.size.y) { return vec4(1.0, 1.0, 1.0, 1.0); } if (ctx.flags & 0x02) != 0 { - var val = clamp(average_energy[u32(y) * ctx.size.x + u32(x)] * ctx.energy_gain, 0.0, 1.0); + var val = clamp(average_energy[u32(pos.y) * ctx.size.x + u32(pos.x)] * ctx.energy_gain, 0.0, 1.0); let scheme_index = u32(val * 3.0); val = val * 3.0 - f32(scheme_index); @@ -86,7 +85,7 @@ fn frag(in: VertexOutput) -> @location(0) vec4 { return vec4(color, 1.0); } - let val = states[index(u32(x), u32(y), ctx.tick % 3)] * ctx.gain; + let val = states[index(u32(pos.x), u32(pos.y), ctx.tick % 3)] * ctx.gain; let color = ( vec3(0.0, 0.0, 1.0) * f32(val > 0.0) + vec3(1.0, 0.0, 0.0) * f32(val < 0.0) diff --git a/src/ui.rs b/src/ui.rs index 360136c..78a54c4 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -2,9 +2,11 @@ use std::time::{Duration, Instant}; use bitflags::Flags; use egui::{emath::Numeric, Color32, Context, DragValue, RichText, Slider, Ui, Window}; +use nalgebra::Vector2; use wgpu::{Buffer, CommandEncoder}; use crate::{ + renderer::Renderer, simulation::{Simulation, SimulationFlags}, FpsTracker, GraphicsContext, }; @@ -31,12 +33,30 @@ impl Gui { ctx: &Context, gc: &GraphicsContext, simulation: &mut Simulation, + render: &mut Renderer, fps: &mut FpsTracker, ) { let now = Instant::now(); let frame_time = now - fps.last_frame; fps.last_frame = now; + let dragging_viewport = ctx.dragged_id().is_none(); + let scale_factor = gc.window.scale_factor() as f32; + + ctx.input(|input| { + let pointer = input.pointer.latest_pos().unwrap_or_default(); + let pointer = Vector2::new(pointer.x, pointer.y) * scale_factor; + + let old_zoom = render.zoom; + render.zoom = (old_zoom + input.smooth_scroll_delta.y / 1000.0).max(0.05); + render.pan += (pointer - render.pan) * (1.0 - (old_zoom / render.zoom)); + + if input.pointer.any_down() && dragging_viewport { + let delta = input.pointer.delta() * scale_factor; + render.pan += Vector2::new(delta.x, delta.y); + } + }); + Window::new("Wave Simulator") .default_width(0.0) .show(ctx, |ui| {