From 8201c8e2a7ff85383ff7606513778253aae7e666 Mon Sep 17 00:00:00 2001 From: Sakarias Johansson Date: Fri, 14 Apr 2023 20:58:23 +0200 Subject: =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20camera=20rotation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before you could only move the position of the camera. - Add support for turning camera by holding down left mouse. - Add support for turning camera with arrow keys. - Add Mouse move callback for key_inputs. --- racer-tracer/Cargo.toml | 1 + racer-tracer/src/camera.rs | 55 ++++++++++++++-- racer-tracer/src/key_inputs.rs | 81 ++++++++++++++++++------ racer-tracer/src/main.rs | 12 +++- racer-tracer/src/scene_controller.rs | 13 +++- racer-tracer/src/scene_controller/interactive.rs | 56 +++++++++++++++- racer-tracer/src/vec3.rs | 19 ++++++ 7 files changed, 202 insertions(+), 35 deletions(-) diff --git a/racer-tracer/Cargo.toml b/racer-tracer/Cargo.toml index ee5bbc8..c9b8ac6 100644 --- a/racer-tracer/Cargo.toml +++ b/racer-tracer/Cargo.toml @@ -20,4 +20,5 @@ slog-term = "2" slog = "2" slog-async = "2" console = "0.15" +glam = "0.23.0" serde = { version = "1", features = ["derive"] } diff --git a/racer-tracer/src/camera.rs b/racer-tracer/src/camera.rs index e6396e8..1ac2396 100644 --- a/racer-tracer/src/camera.rs +++ b/racer-tracer/src/camera.rs @@ -1,3 +1,5 @@ +use glam::{f32::Vec3 as FVec3, Quat}; + use crate::image::Image; use crate::ray::Ray; use crate::util::{degrees_to_radians, random_in_unit_disk}; @@ -14,6 +16,7 @@ pub struct Camera { pub forward: Vec3, pub right: Vec3, pub up: Vec3, + pub scene_up: Vec3, pub lens_radius: f64, pub focus_distance: f64, } @@ -22,7 +25,7 @@ impl Camera { pub fn new( look_from: Vec3, look_at: Vec3, - up: Vec3, + scene_up: Vec3, vfov: f64, image: &Image, aperture: f64, @@ -33,7 +36,7 @@ impl Camera { let viewport_width = image.aspect_ratio * viewport_height; let forward = (look_from - look_at).unit_vector(); - let right = up.cross(&forward).unit_vector(); + let right = scene_up.cross(&forward).unit_vector(); let up = forward.cross(&right); let horizontal = focus_distance * viewport_width * right; @@ -50,6 +53,7 @@ impl Camera { forward, right, up, + scene_up, lens_radius: aperture * 0.5, focus_distance, } @@ -66,17 +70,56 @@ impl Camera { pub fn go_forward(&mut self, go: f64) { self.origin += self.forward * go; - - self.upper_left_corner = self.origin + self.vertical / 2.0 - - self.horizontal / 2.0 - - self.focus_distance * self.forward; + self.update_corner() } pub fn go_right(&mut self, go: f64) { self.origin += self.right * go; + self.update_corner() + } + fn update_corner(&mut self) { self.upper_left_corner = self.origin + self.vertical / 2.0 - self.horizontal / 2.0 - self.focus_distance * self.forward; } + + fn update_directions(&mut self) { + self.forward.unit_vector(); + self.right = self.scene_up.cross(&self.forward).unit_vector(); + self.up = self.forward.cross(&self.right); + self.horizontal = self.focus_distance * self.viewport_width * self.right; + self.vertical = self.focus_distance * self.viewport_height * self.up; + } + + pub fn rotate(&mut self, up: f64, right: f64) { + self.forward = (Quat::from_axis_angle(self.right.into(), right as f32) + * Quat::from_axis_angle(self.scene_up.into(), up as f32) + * FVec3::from(self.forward)) + .into(); + + self.forward.unit_vector(); + self.update_directions(); + self.update_corner(); + } + + pub fn rotate_up(&mut self, go: f64) { + self.forward = (Quat::from_axis_angle(self.right.into(), go as f32) + * FVec3::from(self.forward)) + .into(); + + self.forward.unit_vector(); + self.update_directions(); + self.update_corner(); + } + + pub fn rotate_right(&mut self, go: f64) { + self.forward = (Quat::from_axis_angle(self.scene_up.into(), -go as f32) + * FVec3::from(self.forward)) + .into(); + + self.forward.unit_vector(); + self.update_directions(); + self.update_corner(); + } } diff --git a/racer-tracer/src/key_inputs.rs b/racer-tracer/src/key_inputs.rs index 9a7bde5..b815250 100644 --- a/racer-tracer/src/key_inputs.rs +++ b/racer-tracer/src/key_inputs.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use minifb::{Key, Window}; +use minifb::{Key, MouseButton, MouseMode, Window}; use slog::Logger; use crate::error::TracerError; @@ -11,11 +11,17 @@ pub enum KeyEvent { } type Callback<'func> = Box Result<(), TracerError> + Send + Sync + 'func>; +pub type MouseCallback<'func> = + Box Result<(), TracerError> + Send + Sync + 'func>; pub type KeyCallback<'func> = (KeyEvent, Key, Callback<'func>); pub struct KeyInputs<'func> { is_down_callbacks: HashMap>>, is_released_callbacks: HashMap>>, + mouse_move_callbacks: Vec>, log: Logger, + mouse_x: f32, + mouse_y: f32, + mouse_down: bool, } impl<'func, 'window> KeyInputs<'func> { @@ -23,10 +29,21 @@ impl<'func, 'window> KeyInputs<'func> { KeyInputs { is_down_callbacks: HashMap::new(), is_released_callbacks: HashMap::new(), + mouse_move_callbacks: Vec::new(), log, + mouse_x: 0.0, + mouse_y: 0.0, + mouse_down: false, } } + pub fn mouse_move( + &mut self, + callback: impl Fn(f32, f32) -> Result<(), TracerError> + Send + Sync + 'func, + ) { + self.mouse_move_callbacks.push(Box::new(callback)); + } + pub fn input( event: KeyEvent, key: Key, @@ -74,26 +91,48 @@ impl<'func, 'window> KeyInputs<'func> { callbacks.push(Box::new(closure)); } - pub fn update(&self, window: &'window Window, dt: f64) { - self.is_down_callbacks - .iter() - .filter(|(key, _callbacks)| window.is_key_down(**key)) - .for_each(|(_key, callbacks)| { - callbacks.iter().for_each(|callback| { - if let Err(e) = callback(dt) { - error!(self.log, "Key callback error: {}", e); - } - }) - }); - self.is_released_callbacks - .iter() - .filter(|(key, _callbacks)| window.is_key_released(**key)) - .for_each(|(_key, callbacks)| { - callbacks.iter().for_each(|callback| { - if let Err(e) = callback(dt) { - error!(self.log, "Key callback error: {}", e); + pub fn update(&mut self, window: &'window mut Window, dt: f64) { + if window.is_active() { + if window.get_mouse_down(MouseButton::Left) { + if let Some((x, y)) = window.get_mouse_pos(MouseMode::Pass) { + if !self.mouse_down { + self.mouse_x = x; + self.mouse_y = y; } - }) - }); + self.mouse_move_callbacks.iter().for_each(|cb| { + if let Err(e) = cb(self.mouse_x - x, self.mouse_y - y) { + error!(self.log, "Mouse callback error: {}", e); + } + }); + self.mouse_x = x; + self.mouse_y = y; + } + + self.mouse_down = true; + } else { + self.mouse_down = false; + } + + self.is_down_callbacks + .iter() + .filter(|(key, _callbacks)| window.is_key_down(**key)) + .for_each(|(_key, callbacks)| { + callbacks.iter().for_each(|callback| { + if let Err(e) = callback(dt) { + error!(self.log, "Key callback error: {}", e); + } + }) + }); + self.is_released_callbacks + .iter() + .filter(|(key, _callbacks)| window.is_key_released(**key)) + .for_each(|(_key, callbacks)| { + callbacks.iter().for_each(|callback| { + if let Err(e) = callback(dt) { + error!(self.log, "Key callback error: {}", e); + } + }) + }); + } } } diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs index c31f09b..afdc919 100644 --- a/racer-tracer/src/main.rs +++ b/racer-tracer/src/main.rs @@ -53,6 +53,9 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { let image = image::Image::new(config.screen.width, config.screen.height); let look_from = Vec3::new(13.0, 2.0, 3.0); let look_at = Vec3::new(0.0, 0.0, 0.0); + // TODO: Make camera configurable. + // pos, look_at, fov, aperture, focus distance. + // Also ensure those can be changed during runtime. let camera = Camera::new( look_from, look_at, @@ -71,6 +74,7 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { match &config.scene_controller { config::ConfigSceneController::Interactive => { let camera_speed = 0.000002; + let camera_sensitivity = 0.001; InteractiveScene::new( SceneData { log: log.new(o!("scope" => "scene-controller")), @@ -81,13 +85,17 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { image: image.clone(), }, camera_speed, + camera_sensitivity, ) } } }; let mut inputs = KeyInputs::new(log.new(o!("scope" => "key-inputs"))); - inputs.register_inputs(scene_controller.get_inputs()); + inputs.register_inputs(scene_controller.key_inputs()); + if let Some(mouse_cb) = scene_controller.mouse_input() { + inputs.mouse_move(mouse_cb); + } rayon::scope(|s| { // Render @@ -126,7 +134,7 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> { { let dt = t.elapsed().as_micros() as f64; t = Instant::now(); - inputs.update(&window, dt); + inputs.update(&mut window, dt); window_res = scene_controller diff --git a/racer-tracer/src/scene_controller.rs b/racer-tracer/src/scene_controller.rs index 4a867f5..3ca5b70 100644 --- a/racer-tracer/src/scene_controller.rs +++ b/racer-tracer/src/scene_controller.rs @@ -3,8 +3,13 @@ pub mod interactive; use slog::Logger; use crate::{ - camera::Camera, config::Config, error::TracerError, image::Image, key_inputs::KeyCallback, - scene::Scene, terminal::Terminal, + camera::Camera, + config::Config, + error::TracerError, + image::Image, + key_inputs::{KeyCallback, MouseCallback}, + scene::Scene, + terminal::Terminal, }; pub fn create_screen_buffer(image: &Image) -> Vec { @@ -23,7 +28,9 @@ pub struct SceneData { pub trait SceneController: Send + Sync { // Return a vector of key callbacks. The provided closure will be // called when the corresponding key is release/pressed. - fn get_inputs(&self) -> Vec; + fn key_inputs(&self) -> Vec; + + fn mouse_input(&self) -> Option; // Render function fn render(&self) -> Result<(), TracerError>; diff --git a/racer-tracer/src/scene_controller/interactive.rs b/racer-tracer/src/scene_controller/interactive.rs index 4624ba1..5fdecfc 100644 --- a/racer-tracer/src/scene_controller/interactive.rs +++ b/racer-tracer/src/scene_controller/interactive.rs @@ -13,7 +13,7 @@ use crate::{ error::TracerError, image::Image, image_action::ImageAction, - key_inputs::{KeyCallback, KeyEvent, KeyInputs}, + key_inputs::{KeyCallback, KeyEvent, KeyInputs, MouseCallback}, renderer::{RenderData, Renderer}, scene::Scene, terminal::Terminal, @@ -25,6 +25,8 @@ pub struct InteractiveScene<'renderer, 'action> { screen_buffer: RwLock>, preview_buffer: RwLock>, camera_speed: f64, + camera_sensitivity: f32, + camera_key_sensitivity: f32, render_image_event: SignalEvent, buffer_updated: SignalEvent, stop_event: SignalEvent, @@ -41,11 +43,13 @@ pub struct InteractiveScene<'renderer, 'action> { } impl<'renderer, 'action> InteractiveScene<'renderer, 'action> { - pub fn new(scene_data: SceneData, camera_speed: f64) -> Self { + pub fn new(scene_data: SceneData, camera_speed: f64, camera_sensitivity: f32) -> Self { Self { screen_buffer: RwLock::new(create_screen_buffer(&scene_data.image)), preview_buffer: RwLock::new(create_screen_buffer(&scene_data.image)), camera_speed, + camera_sensitivity, + camera_key_sensitivity: camera_sensitivity * 0.0005, // Just scale it for now. render_image_event: SignalEvent::manual(false), buffer_updated: SignalEvent::manual(false), stop_event: SignalEvent::manual(false), @@ -64,8 +68,40 @@ impl<'renderer, 'action> InteractiveScene<'renderer, 'action> { } impl<'renderer, 'action> SceneController for InteractiveScene<'renderer, 'action> { - fn get_inputs(&self) -> Vec { + fn key_inputs(&self) -> Vec { vec![ + KeyInputs::input(KeyEvent::Down, Key::Left, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.rotate_right(-dt * self.camera_key_sensitivity as f64); + }) + }), + KeyInputs::input(KeyEvent::Down, Key::Right, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.rotate_right(dt * self.camera_key_sensitivity as f64); + }) + }), + KeyInputs::input(KeyEvent::Down, Key::Up, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.rotate_up(dt * self.camera_key_sensitivity as f64); + }) + }), + KeyInputs::input(KeyEvent::Down, Key::Down, |dt| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.rotate_up(-dt * self.camera_key_sensitivity as f64); + }) + }), KeyInputs::input(KeyEvent::Release, Key::R, |_| { self.render_image_event.signal(); Ok(()) @@ -105,6 +141,20 @@ impl<'renderer, 'action> SceneController for InteractiveScene<'renderer, 'action ] } + fn mouse_input(&self) -> Option { + Some(Box::new(|x, y| { + self.camera + .write() + .map_err(|e| TracerError::KeyError(e.to_string())) + .map(|mut cam| { + cam.rotate( + (x * self.camera_sensitivity) as f64, + (y * self.camera_sensitivity) as f64, + ) + }) + })) + } + fn get_buffer(&self) -> Result>, TracerError> { self.buffer_updated .wait_timeout(Duration::from_secs(0)) diff --git a/racer-tracer/src/vec3.rs b/racer-tracer/src/vec3.rs index 9d04cb4..6877eea 100644 --- a/racer-tracer/src/vec3.rs +++ b/racer-tracer/src/vec3.rs @@ -1,3 +1,6 @@ +//TODO: Replace this with glam. +use glam::f32::Vec3 as FVec3; + use std::{fmt, ops}; use serde::Deserialize; @@ -326,6 +329,22 @@ impl fmt::Display for Vec3 { } } +impl From for Vec3 { + fn from(v: FVec3) -> Self { + Vec3::new(v.x as f64, v.y as f64, v.z as f64) + } +} + +impl From for FVec3 { + fn from(v: Vec3) -> Self { + FVec3 { + x: v.data[0] as f32, + y: v.data[1] as f32, + z: v.data[2] as f32, + } + } +} + impl std::fmt::Debug for Vec3 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Vec3").field("data", &self.data).finish() -- cgit v1.2.3