diff --git a/src/example.rs b/src/example.rs index 12bf3332..c8d5d576 100644 --- a/src/example.rs +++ b/src/example.rs @@ -5,8 +5,8 @@ pub const MUNICH_Y: u32 = 11360; pub const MUNICH_Z: u8 = 15; pub fn fetch_munich_tiles(cache: &Cache) { - for x in 0..5 { - for y in 0..5 { + for x in 0..15 { + for y in 0..15 { cache.fetch((MUNICH_X + x, MUNICH_Y + y, MUNICH_Z).into()) } } diff --git a/src/input/camera_controller.rs b/src/input/camera_controller.rs index c011dbd4..de3da83d 100644 --- a/src/input/camera_controller.rs +++ b/src/input/camera_controller.rs @@ -1,36 +1,32 @@ use std::f32::consts::FRAC_PI_2; -use cgmath::InnerSpace; +use crate::render::camera; +use cgmath::{EuclideanSpace, InnerSpace, Matrix4, SquareMatrix, Vector2, Vector3, Vector4}; +use log::info; const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001; #[derive(Debug)] pub struct CameraController { - amount_left: f32, - amount_right: f32, - amount_forward: f32, - amount_backward: f32, - amount_up: f32, - amount_down: f32, - rotate_horizontal: f32, - rotate_vertical: f32, - scroll: f32, - speed: f32, - sensitivity: f32, + translate_x: f64, + translate_y: f64, + direct_translate_x: f64, + direct_translate_y: f64, + + zoom: f64, + + speed: f64, + sensitivity: f64, } impl CameraController { - pub fn new(speed: f32, sensitivity: f32) -> Self { + pub fn new(speed: f64, sensitivity: f64) -> Self { Self { - amount_left: 0.0, - amount_right: 0.0, - amount_forward: 0.0, - amount_backward: 0.0, - amount_up: 0.0, - amount_down: 0.0, - rotate_horizontal: 0.0, - rotate_vertical: 0.0, - scroll: 0.0, + translate_x: 0.0, + translate_y: 0.0, + direct_translate_x: 0.0, + direct_translate_y: 0.0, + zoom: 0.0, speed, sensitivity, } @@ -42,58 +38,107 @@ impl CameraController { state: winit::event::ElementState, ) -> bool { let amount = if state == winit::event::ElementState::Pressed { - 1.0 + 10.0 * self.sensitivity // left, right is the same as panning 10px } else { 0.0 }; match key { winit::event::VirtualKeyCode::W | winit::event::VirtualKeyCode::Up => { - self.amount_forward = amount; + self.translate_y += amount; true } winit::event::VirtualKeyCode::S | winit::event::VirtualKeyCode::Down => { - self.amount_backward = amount; + self.translate_y -= amount; true } winit::event::VirtualKeyCode::A | winit::event::VirtualKeyCode::Left => { - self.amount_left = amount; + self.translate_x -= amount; true } winit::event::VirtualKeyCode::D | winit::event::VirtualKeyCode::Right => { - self.amount_right = amount; + self.translate_x += amount; true } winit::event::VirtualKeyCode::Space => { - self.amount_up = amount; - true - } - winit::event::VirtualKeyCode::LShift => { - self.amount_down = amount; + self.translate_y = amount; true } _ => false, } } - pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) { - self.rotate_horizontal = mouse_dx as f32; - self.rotate_vertical = mouse_dy as f32; + pub fn process_mouse( + &mut self, + start_cam_x: f64, + start_cam_y: f64, + mouse_dx: f64, + mouse_dy: f64, + width: f64, + height: f64, + camera: &mut camera::Camera, + view_proj: &Matrix4, + ) { + info!("mouse_dx {} mouse_dy {}", mouse_dx, mouse_dy); + + let origin = Vector2::new(0.0, 0.0); + let screen = Vector2::new(mouse_dx, mouse_dy); + let camera_pos = &camera.position.to_vec(); + let world = Self::screen_to_world(&origin, width, height, camera_pos, &view_proj) + - Self::screen_to_world(&screen, width, height, camera_pos, &view_proj); + + info!("world {:?}", world); + + //self.direct_translate_x -= world.x; + //self.direct_translate_y += world.y; + camera.position.x = start_cam_x - world.x; + camera.position.y = start_cam_y + world.y; + } + + fn screen_to_world( + screen: &Vector2, + width: f64, + height: f64, + camera_pos: &Vector3, + view_proj: &Matrix4, + ) -> Vector4 { + let min_depth = 0.0; + let max_depth = 1.0; + + let x = 0.0; + let y = 0.0; + let ox = x + width / 2.0; + let oy = y + height / 2.0; + let oz = min_depth; + let pz = max_depth - min_depth; + + // Adapted from: https://docs.microsoft.com/en-us/windows/win32/direct3d9/viewports-and-clipping#viewport-rectangle + let direct_x = Matrix4::from_cols( + Vector4::new(width as f64 / 2.0, 0.0, 0.0, 0.0), + Vector4::new(0.0, height as f64 / 2.0, 0.0, 0.0), + Vector4::new(0.0, 0.0, pz, 0.0), + Vector4::new(ox, oy, oz, 1.0), + ); + + let screen_hom = Vector4::new(screen.x, screen.y, 1.0, 1.0) * camera_pos.z; + let result = direct_x.invert().unwrap() * screen_hom; + let world_pos = view_proj.invert().unwrap() * result; + world_pos } pub fn process_touch(&mut self, touch_dx: f64, touch_dy: f64) { - self.amount_right += touch_dx as f32; - self.amount_up += touch_dy as f32; + self.translate_x += touch_dx as f64 * self.sensitivity; + self.translate_y += touch_dy as f64 * self.sensitivity; } pub fn process_scroll(&mut self, delta: &winit::event::MouseScrollDelta) { - self.scroll = -match delta { + self.zoom = -match delta { // I'm assuming a line is about 100 pixels - winit::event::MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0, + winit::event::MouseScrollDelta::LineDelta(_, scroll) => *scroll as f64 * 100.0, winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition { y: scroll, .. - }) => *scroll as f32, - }; + }) => *scroll, + } * self.sensitivity; } pub fn update_camera( @@ -101,44 +146,27 @@ impl CameraController { camera: &mut crate::render::camera::Camera, dt: std::time::Duration, ) { - let dt = dt.as_secs_f32(); + camera.position.x += self.direct_translate_x; + camera.position.y += self.direct_translate_y; + self.direct_translate_x = 0.0; + self.direct_translate_y = 0.0; - // Move forward/backward and left/right - let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos(); - let forward = cgmath::Vector3::new(yaw_cos, 0.0, yaw_sin).normalize(); - let right = cgmath::Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize(); - camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt; - camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt; + let dt = dt.as_secs_f64() * self.speed; + + let dy = self.translate_y * dt; + camera.position.y += dy; + let dx = self.translate_x * dt; + camera.position.x += dx; // Move in/out (aka. "zoom") // Note: this isn't an actual zoom. The camera's position // changes when zooming. I've added this to make it easier // to get closer to an object you want to focus on. - let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos(); - let scrollward = - cgmath::Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize(); - camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt; - self.scroll = 0.0; + let dz = self.zoom * dt; + camera.position.z += dz; - // Move up/down. Since we don't use roll, we can just - // modify the y coordinate directly. - camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt; - - // Rotate - camera.yaw += cgmath::Rad(self.rotate_horizontal) * self.sensitivity * dt; - camera.pitch += cgmath::Rad(-self.rotate_vertical) * self.sensitivity * dt; - - // If process_mouse isn't called every frame, these values - // will not get set to zero, and the camera will rotate - // when moving in a non cardinal direction. - self.rotate_horizontal = 0.0; - self.rotate_vertical = 0.0; - - // Keep the camera's angle from going too high/low. - if camera.pitch < -cgmath::Rad(SAFE_FRAC_PI_2) { - camera.pitch = -cgmath::Rad(SAFE_FRAC_PI_2); - } else if camera.pitch > cgmath::Rad(SAFE_FRAC_PI_2) { - camera.pitch = cgmath::Rad(SAFE_FRAC_PI_2); - } + self.zoom -= dz; + self.translate_x -= dx; + self.translate_y -= dy; } } diff --git a/src/input/mod.rs b/src/input/mod.rs index 429656c0..97de1fcd 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,3 +1,4 @@ +use cgmath::Vector3; use std::time::Duration; use winit::event::{ DeviceEvent, ElementState, KeyboardInput, MouseButton, TouchPhase, WindowEvent, @@ -5,6 +6,7 @@ use winit::event::{ use winit::window::Window; use crate::input::camera_controller::CameraController; +use crate::render::camera::Camera; use crate::render::state::State; mod camera_controller; @@ -13,38 +15,78 @@ pub struct InputHandler { camera_controller: CameraController, last_touch: Option<(f64, f64)>, + start_camera_pos: Option>, mouse_pressed: bool, target_stroke_width: f32, } impl InputHandler { pub fn new() -> Self { - let camera_controller = CameraController::new(5000.0, 0.2); + let camera_controller = CameraController::new(5.0, 100.0); Self { target_stroke_width: 1.0, + start_camera_pos: None, last_touch: None, mouse_pressed: false, camera_controller, } } - pub fn device_input(&mut self, event: &DeviceEvent, window: &Window) -> bool { + pub fn device_input( + &mut self, + event: &DeviceEvent, + state: &mut State, + window: &Window, + ) -> bool { match event { DeviceEvent::MouseMotion { delta } => { - if self.mouse_pressed { + /* if self.mouse_pressed { + let view_proj = state.camera.calc_view_proj(&state.perspective); self.camera_controller.process_mouse( delta.0 / window.scale_factor(), delta.1 / window.scale_factor(), + state.size.width as f64, + state.size.height as f64, + &mut state.camera, + &view_proj, ); - } + }*/ true } _ => false, } } - pub fn window_input(&mut self, event: &WindowEvent, window: &Window) -> bool { + pub fn window_input( + &mut self, + event: &WindowEvent, + state: &mut State, + window: &Window, + ) -> bool { match event { + WindowEvent::CursorMoved { position, .. } => { + if self.mouse_pressed { + if let Some(start) = self.last_touch { + let delta_x = start.0 - position.x; + let delta_y = start.1 - position.y; + let view_proj = state.camera.calc_view_proj(&state.perspective); + self.camera_controller.process_mouse( + self.start_camera_pos.unwrap().x, + self.start_camera_pos.unwrap().y, + delta_x, + delta_y, + state.size.width as f64, + state.size.height as f64, + &mut state.camera, + &view_proj, + ); + } else { + self.last_touch = Some((position.x, position.y)); + self.start_camera_pos = Some(state.camera.position); + } + } + true + } WindowEvent::KeyboardInput { input: KeyboardInput { @@ -67,19 +109,29 @@ impl InputHandler { WindowEvent::Touch(touch) => { match touch.phase { TouchPhase::Started => { - self.last_touch = Some((touch.location.x, touch.location.y)) + self.last_touch = Some((touch.location.x, touch.location.y)); + self.start_camera_pos = Some(state.camera.position); } - TouchPhase::Moved | TouchPhase::Ended => { + TouchPhase::Ended => { + self.last_touch = None; + self.start_camera_pos = None; + } + TouchPhase::Moved => { if let Some(start) = self.last_touch { let delta_x = start.0 - touch.location.x; let delta_y = start.1 - touch.location.y; - self.camera_controller.process_touch( - delta_x / window.scale_factor(), - delta_y / window.scale_factor(), + let view_proj = state.camera.calc_view_proj(&state.perspective); + self.camera_controller.process_mouse( + self.start_camera_pos.unwrap().x, + self.start_camera_pos.unwrap().y, + delta_x, + delta_y, + state.size.width as f64, + state.size.height as f64, + &mut state.camera, + &view_proj, ); } - - self.last_touch = Some((touch.location.x, touch.location.y)) } TouchPhase::Cancelled => {} } @@ -96,6 +148,11 @@ impl InputHandler { .. } => { self.mouse_pressed = *state == ElementState::Pressed; + + if !self.mouse_pressed { + self.last_touch = None; + self.start_camera_pos = None; + } true } _ => false, diff --git a/src/main_loop.rs b/src/main_loop.rs index be4179f6..5a59eda3 100644 --- a/src/main_loop.rs +++ b/src/main_loop.rs @@ -45,14 +45,14 @@ pub async fn setup(window: winit::window::Window, event_loop: EventLoop<()>, cac .. // We're not using device_id currently } => { trace!("{:?}", event); - input.device_input(event,&window); + input.device_input(event, state, &window); } Event::WindowEvent { ref event, window_id, } if window_id == window.id() => { - if !input.window_input(event, &window) { + if !input.window_input(event, state, &window) { match event { WindowEvent::CloseRequested | WindowEvent::KeyboardInput { diff --git a/src/render/camera.rs b/src/render/camera.rs index 1cd41d61..23612c16 100644 --- a/src/render/camera.rs +++ b/src/render/camera.rs @@ -1,9 +1,10 @@ use cgmath::prelude::*; +use cgmath::Matrix4; use crate::render::shader_ffi::CameraUniform; #[rustfmt::skip] -pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, @@ -12,16 +13,16 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( #[derive(Debug)] pub struct Camera { - pub position: cgmath::Point3, - pub yaw: cgmath::Rad, - pub pitch: cgmath::Rad, + pub position: cgmath::Point3, + pub yaw: cgmath::Rad, + pub pitch: cgmath::Rad, } impl Camera { pub fn new< - V: Into>, - Y: Into>, - P: Into>, + V: Into>, + Y: Into>, + P: Into>, >( position: V, yaw: Y, @@ -34,7 +35,7 @@ impl Camera { } } - pub fn calc_matrix(&self) -> cgmath::Matrix4 { + pub fn calc_matrix(&self) -> cgmath::Matrix4 { cgmath::Matrix4::look_to_rh( self.position, cgmath::Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()) @@ -42,29 +43,37 @@ impl Camera { cgmath::Vector3::unit_y(), ) } + + pub fn calc_view_proj(&self, perspective: &Perspective) -> Matrix4 { + perspective.calc_matrix() * self.calc_matrix() + } + pub fn create_camera_uniform(&self, perspective: &Perspective) -> CameraUniform { - let view_proj = (perspective.calc_matrix() * self.calc_matrix()); - CameraUniform::new(view_proj.into(), self.position.to_homogeneous().into()) + let view_proj = self.calc_view_proj(perspective); + CameraUniform::new( + view_proj.cast::().unwrap().into(), + self.position.to_homogeneous().cast::().unwrap().into(), + ) } } pub struct Perspective { - aspect: f32, - fovy: cgmath::Rad, - znear: f32, - zfar: f32, + aspect: f64, + fovy: cgmath::Rad, + znear: f64, + zfar: f64, } impl Perspective { - pub fn new>>( + pub fn new>>( width: u32, height: u32, fovy: F, - znear: f32, - zfar: f32, + znear: f64, + zfar: f64, ) -> Self { Self { - aspect: width as f32 / height as f32, + aspect: width as f64 / height as f64, fovy: fovy.into(), znear, zfar, @@ -72,10 +81,10 @@ impl Perspective { } pub fn resize(&mut self, width: u32, height: u32) { - self.aspect = width as f32 / height as f32; + self.aspect = width as f64 / height as f64; } - pub fn calc_matrix(&self) -> cgmath::Matrix4 { + pub fn calc_matrix(&self) -> cgmath::Matrix4 { OPENGL_TO_WGPU_MATRIX * cgmath::perspective(self.fovy, self.aspect, self.znear, self.zfar) } } @@ -137,6 +146,7 @@ mod tests { Vector4::new(ox, oy, oz, 1.0), ); let screen_hom = direct_x * result; + println!("screen_hom: {:?}", screen_hom); let screen = Vector3::new( screen_hom.x / screen_hom.w, screen_hom.y / screen_hom.w, @@ -144,8 +154,12 @@ mod tests { ); println!("screen: {:?}", screen); + let screen_hom2 = Vector4::new(screen.x, screen.y, 1.0, 1.0) * 5000.0; + println!("screen_hom2: {:?}", screen_hom2); let result = direct_x.invert().unwrap() * screen_hom; - println!("result: {:?}", result); + println!("result screen_hom: {:?}", result); + let result = direct_x.invert().unwrap() * screen_hom2; + println!("result screen_hom2: {:?}", result); let world_pos = view_proj.invert().unwrap() * result; println!("world_pos: {:?}", world_pos); } diff --git a/src/render/state.rs b/src/render/state.rs index c4fc4539..63a6cd1b 100644 --- a/src/render/state.rs +++ b/src/render/state.rs @@ -47,7 +47,7 @@ pub struct State { surface_config: wgpu::SurfaceConfiguration, suspended: bool, - size: winit::dpi::PhysicalSize, + pub size: winit::dpi::PhysicalSize, render_pipeline: wgpu::RenderPipeline, mask_pipeline: wgpu::RenderPipeline, @@ -67,7 +67,7 @@ pub struct State { tile_mask_instances_buffer: wgpu::Buffer, pub camera: camera::Camera, - perspective: camera::Perspective, + pub perspective: camera::Perspective, pub scene: SceneParams, }