summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--racer-tracer/config.yml2
-rw-r--r--racer-tracer/src/config.rs32
-rw-r--r--racer-tracer/src/image_action.rs7
-rw-r--r--racer-tracer/src/image_action/png.rs1
-rw-r--r--racer-tracer/src/image_action/wait_for_signal.rs5
-rw-r--r--racer-tracer/src/key_inputs.rs67
-rw-r--r--racer-tracer/src/main.rs177
-rw-r--r--racer-tracer/src/renderer.rs22
-rw-r--r--racer-tracer/src/renderer/cpu.rs3
-rw-r--r--racer-tracer/src/renderer/cpu_scaled.rs3
-rw-r--r--racer-tracer/src/scene_controller.rs37
-rw-r--r--racer-tracer/src/scene_controller/interactive.rs209
12 files changed, 412 insertions, 153 deletions
diff --git a/racer-tracer/config.yml b/racer-tracer/config.yml
index e658c1c..e0f42bf 100644
--- a/racer-tracer/config.yml
+++ b/racer-tracer/config.yml
@@ -23,4 +23,4 @@ loader:
image_output_dir: "../"
image_action:
- SavePng # (SavePng, WaitForSignal)
+ WaitForSignal # (SavePng, WaitForSignal)
diff --git a/racer-tracer/src/config.rs b/racer-tracer/src/config.rs
index 3992176..7d28ebe 100644
--- a/racer-tracer/src/config.rs
+++ b/racer-tracer/src/config.rs
@@ -6,13 +6,13 @@ use structopt::StructOpt;
use crate::error::TracerError;
-#[derive(Default, Debug, Deserialize)]
+#[derive(Default, Clone, Debug, Deserialize)]
pub struct Screen {
pub height: usize,
pub width: usize,
}
-#[derive(Default, Debug, Deserialize)]
+#[derive(Default, Clone, Debug, Deserialize)]
pub struct RenderConfigData {
pub samples: usize,
pub max_depth: usize,
@@ -93,6 +93,23 @@ pub enum ImageAction {
SavePng,
}
+#[derive(StructOpt, Debug, Clone, Deserialize, Default)]
+pub enum ConfigSceneController {
+ #[default]
+ Interactive,
+}
+
+#[derive(StructOpt, Debug, Clone, Deserialize, Default)]
+pub enum Renderer {
+ #[default]
+ Cpu,
+ CpuPreview,
+}
+
+fn default_preview_renderer() -> Renderer {
+ Renderer::CpuPreview
+}
+
impl FromStr for ImageAction {
type Err = TracerError;
@@ -105,7 +122,7 @@ impl FromStr for ImageAction {
}
}
-#[derive(Default, Debug, Deserialize)]
+#[derive(Default, Clone, Debug, Deserialize)]
pub struct Config {
#[serde(default)]
pub preview: RenderConfigData,
@@ -124,6 +141,15 @@ pub struct Config {
#[serde(default)]
pub image_output_dir: Option<PathBuf>,
+
+ #[serde(default)]
+ pub scene_controller: ConfigSceneController,
+
+ #[serde(default)]
+ pub renderer: Renderer,
+
+ #[serde(default = "default_preview_renderer")]
+ pub preview_renderer: Renderer,
}
impl Config {
diff --git a/racer-tracer/src/image_action.rs b/racer-tracer/src/image_action.rs
index 3f984a1..9156b6d 100644
--- a/racer-tracer/src/image_action.rs
+++ b/racer-tracer/src/image_action.rs
@@ -18,6 +18,7 @@ pub trait ImageAction: Send + Sync {
fn action(
&self,
screen_buffer: &RwLock<Vec<u32>>,
+ cancel_event: &SignalEvent,
event: &SignalEvent,
config: &Config,
log: Logger,
@@ -25,11 +26,11 @@ pub trait ImageAction: Send + Sync {
) -> Result<(), TracerError>;
}
-impl From<&CImageAction> for Box<dyn ImageAction> {
+impl From<&CImageAction> for &dyn ImageAction {
fn from(image_action: &CImageAction) -> Self {
match image_action {
- CImageAction::WaitForSignal => Box::new(WaitForSignal {}),
- CImageAction::SavePng => Box::new(SavePng {}),
+ CImageAction::WaitForSignal => &WaitForSignal {} as &dyn ImageAction,
+ CImageAction::SavePng => &SavePng {} as &dyn ImageAction,
}
}
}
diff --git a/racer-tracer/src/image_action/png.rs b/racer-tracer/src/image_action/png.rs
index dbed285..5b2bf1a 100644
--- a/racer-tracer/src/image_action/png.rs
+++ b/racer-tracer/src/image_action/png.rs
@@ -14,6 +14,7 @@ impl ImageAction for SavePng {
fn action(
&self,
screen_buffer: &RwLock<Vec<u32>>,
+ _cancel_event: &SignalEvent,
event: &SignalEvent,
config: &Config,
log: Logger,
diff --git a/racer-tracer/src/image_action/wait_for_signal.rs b/racer-tracer/src/image_action/wait_for_signal.rs
index 0ffe5e7..1e31200 100644
--- a/racer-tracer/src/image_action/wait_for_signal.rs
+++ b/racer-tracer/src/image_action/wait_for_signal.rs
@@ -1,4 +1,4 @@
-use std::sync::RwLock;
+use std::{sync::RwLock, time::Duration};
use slog::Logger;
use synchronoise::SignalEvent;
@@ -17,6 +17,7 @@ impl ImageAction for WaitForSignal {
fn action(
&self,
_screen_buffer: &RwLock<Vec<u32>>,
+ cancel_event: &SignalEvent,
event: &SignalEvent,
_config: &Config,
_log: Logger,
@@ -24,7 +25,7 @@ impl ImageAction for WaitForSignal {
) -> Result<(), TracerError> {
if !event.status() {
write_term!(term, "Press R to resume.");
- event.wait();
+ while !event.wait_timeout(Duration::from_secs(1)) && !cancel_event.status() {}
}
event.reset();
Ok(())
diff --git a/racer-tracer/src/key_inputs.rs b/racer-tracer/src/key_inputs.rs
index 1e3505e..9a7bde5 100644
--- a/racer-tracer/src/key_inputs.rs
+++ b/racer-tracer/src/key_inputs.rs
@@ -5,15 +5,20 @@ use slog::Logger;
use crate::error::TracerError;
-pub type KeyClosure<'a> = &'a (dyn Fn(f64) -> Result<(), TracerError> + Send + Sync);
+pub enum KeyEvent {
+ Release,
+ Down,
+}
-pub struct KeyInputs<'a> {
- is_down_callbacks: HashMap<Key, Vec<KeyClosure<'a>>>,
- is_released_callbacks: HashMap<Key, Vec<KeyClosure<'a>>>,
+type Callback<'func> = Box<dyn Fn(f64) -> Result<(), TracerError> + Send + Sync + 'func>;
+pub type KeyCallback<'func> = (KeyEvent, Key, Callback<'func>);
+pub struct KeyInputs<'func> {
+ is_down_callbacks: HashMap<Key, Vec<Callback<'func>>>,
+ is_released_callbacks: HashMap<Key, Vec<Callback<'func>>>,
log: Logger,
}
-impl<'a> KeyInputs<'a> {
+impl<'func, 'window> KeyInputs<'func> {
pub fn new(log: Logger) -> Self {
KeyInputs {
is_down_callbacks: HashMap::new(),
@@ -22,35 +27,69 @@ impl<'a> KeyInputs<'a> {
}
}
- pub fn release(&mut self, key: Key, closure: KeyClosure<'a>) {
+ pub fn input(
+ event: KeyEvent,
+ key: Key,
+ callback: impl Fn(f64) -> Result<(), TracerError> + Send + Sync + 'func,
+ ) -> KeyCallback<'func> {
+ (event, key, Box::new(callback))
+ }
+
+ pub fn register_inputs(&mut self, inputs: Vec<KeyCallback<'func>>) {
+ inputs.into_iter().for_each(|(ev, key, closure)| match ev {
+ KeyEvent::Release => {
+ let callbacks = self
+ .is_released_callbacks
+ .entry(key)
+ .or_insert_with(Vec::new);
+ callbacks.push(closure);
+ }
+ KeyEvent::Down => {
+ let callbacks = self.is_down_callbacks.entry(key).or_insert_with(Vec::new);
+ callbacks.push(closure);
+ }
+ })
+ }
+
+ #[allow(dead_code)]
+ pub fn release(
+ &mut self,
+ key: Key,
+ closure: impl Fn(f64) -> Result<(), TracerError> + Send + Sync + 'func,
+ ) {
let callbacks = self
.is_released_callbacks
.entry(key)
.or_insert_with(Vec::new);
- callbacks.push(closure);
+ callbacks.push(Box::new(closure));
}
- pub fn down(&mut self, key: Key, closure: KeyClosure<'a>) {
+ #[allow(dead_code)]
+ pub fn down(
+ &mut self,
+ key: Key,
+ closure: impl Fn(f64) -> Result<(), TracerError> + Send + Sync + 'func,
+ ) {
let callbacks = self.is_down_callbacks.entry(key).or_insert_with(Vec::new);
- callbacks.push(closure);
+ callbacks.push(Box::new(closure));
}
- pub fn update(&mut self, window: &Window, dt: f64) {
+ pub fn update(&self, window: &'window Window, dt: f64) {
self.is_down_callbacks
- .iter_mut()
+ .iter()
.filter(|(key, _callbacks)| window.is_key_down(**key))
.for_each(|(_key, callbacks)| {
- callbacks.iter_mut().for_each(|callback| {
+ callbacks.iter().for_each(|callback| {
if let Err(e) = callback(dt) {
error!(self.log, "Key callback error: {}", e);
}
})
});
self.is_released_callbacks
- .iter_mut()
+ .iter()
.filter(|(key, _callbacks)| window.is_key_released(**key))
.for_each(|(_key, callbacks)| {
- callbacks.iter_mut().for_each(|callback| {
+ 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 f950000..c31f09b 100644
--- a/racer-tracer/src/main.rs
+++ b/racer-tracer/src/main.rs
@@ -10,6 +10,7 @@ mod material;
mod ray;
mod renderer;
mod scene;
+mod scene_controller;
mod terminal;
mod util;
mod vec3;
@@ -25,9 +26,7 @@ use std::{
convert::TryFrom,
fs::OpenOptions,
path::Path,
- sync::RwLock,
time::{Duration, Instant},
- vec::Vec,
};
use minifb::{Key, Window, WindowOptions};
@@ -37,7 +36,7 @@ use synchronoise::SignalEvent;
use terminal::Terminal;
use crate::{
- renderer::{RenderData, Renderer},
+ scene_controller::{interactive::InteractiveScene, SceneController, SceneData},
vec3::Vec3,
};
@@ -45,22 +44,16 @@ use crate::{
camera::Camera,
config::{Args, Config},
error::TracerError,
- image_action::ImageAction,
key_inputs::KeyInputs,
- renderer::{cpu::CpuRenderer, cpu_scaled::CpuRendererScaled},
scene::Scene,
};
fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> {
info!(log, "Starting racer-tracer {}", env!("CARGO_PKG_VERSION"));
- let renderer: &dyn Renderer = &CpuRenderer {} as &dyn Renderer;
- let renderer_preview: &dyn Renderer = &CpuRendererScaled {} as &dyn Renderer;
let image = image::Image::new(config.screen.width, config.screen.height);
- let screen_buffer: RwLock<Vec<u32>> = RwLock::new(vec![0; image.width * image.height]);
let look_from = Vec3::new(13.0, 2.0, 3.0);
let look_at = Vec3::new(0.0, 0.0, 0.0);
- let camera_speed = 2.0;
- let camera = RwLock::new(Camera::new(
+ let camera = Camera::new(
look_from,
look_at,
Vec3::new(0.0, 1.0, 0.0),
@@ -68,120 +61,42 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> {
&image,
0.1,
10.0,
- ));
-
+ );
let scene = Scene::try_new(&config.loader)?;
-
let mut window_res: Result<(), TracerError> = Ok(());
let mut render_res: Result<(), TracerError> = Ok(());
-
- let render_image = SignalEvent::manual(false);
let exit = SignalEvent::manual(false);
- let image_action: Box<dyn ImageAction> = (&config.image_action).into();
-
- // Setting up controls
- let mut key_inputs = KeyInputs::new(log.new(o!("scope" => "key-intputs")));
- let render_image_fn = |_| {
- render_image.signal();
- Ok(())
- };
- key_inputs.release(Key::R, &render_image_fn);
-
- let go_forward = |dt: f64| {
- camera
- .write()
- .map_err(|e| TracerError::KeyError(e.to_string()))
- .map(|mut cam| {
- cam.go_forward(-dt * camera_speed);
- })
- };
- key_inputs.down(Key::W, &go_forward);
-
- let go_back = |dt: f64| {
- camera
- .write()
- .map_err(|e| TracerError::KeyError(e.to_string()))
- .map(|mut cam| {
- cam.go_forward(dt * camera_speed);
- })
+ let scene_controller = {
+ match &config.scene_controller {
+ config::ConfigSceneController::Interactive => {
+ let camera_speed = 0.000002;
+ InteractiveScene::new(
+ SceneData {
+ log: log.new(o!("scope" => "scene-controller")),
+ term,
+ config: config.clone(),
+ scene,
+ camera,
+ image: image.clone(),
+ },
+ camera_speed,
+ )
+ }
+ }
};
- key_inputs.down(Key::S, &go_back);
- let go_left = |dt: f64| {
- camera
- .write()
- .map_err(|e| TracerError::KeyError(e.to_string()))
- .map(|mut cam| {
- cam.go_right(-dt * camera_speed);
- })
- };
- key_inputs.down(Key::A, &go_left);
-
- let go_right = |dt: f64| {
- camera
- .write()
- .map_err(|e| TracerError::KeyError(e.to_string()))
- .map(|mut cam| {
- cam.go_right(dt * camera_speed);
- })
- };
- key_inputs.down(Key::D, &go_right);
+ let mut inputs = KeyInputs::new(log.new(o!("scope" => "key-inputs")));
+ inputs.register_inputs(scene_controller.get_inputs());
rayon::scope(|s| {
+ // Render
s.spawn(|_| {
while render_res.is_ok() {
render_res = (!exit.wait_timeout(Duration::from_secs(0)))
.then_some(|| ())
.ok_or(TracerError::ExitEvent)
- .and_then(|_| {
- render_image
- .wait_timeout(Duration::from_secs(0))
- .then_some(|| ())
- .map_or_else(
- || {
- render_image.reset();
- // Render preview
- renderer_preview.render(RenderData {
- buffer: &screen_buffer,
- camera: &camera,
- image: &image,
- scene: &scene,
- config: &config,
- cancel_event: None,
- })
- },
- |_| {
- render_image.reset();
- let render_time = Instant::now();
- renderer
- .render(RenderData {
- buffer: &screen_buffer,
- camera: &camera,
- image: &image,
- scene: &scene,
- config: &config,
- cancel_event: Some(&render_image),
- })
- .and_then(|_| {
- info!(
- log,
- "It took {} seconds to render the image.",
- Instant::now()
- .duration_since(render_time)
- .as_secs()
- );
- image_action.action(
- &screen_buffer,
- &render_image,
- &config,
- log.new(o!("scope" => "image-action")),
- &term,
- )
- })
- },
- )
- });
+ .and_then(|_| scene_controller.render());
}
if render_res.is_err() {
@@ -189,6 +104,7 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> {
}
});
+ // Update
s.spawn(|_| {
window_res = Window::new(
"racer-tracer",
@@ -201,27 +117,32 @@ fn run(config: Config, log: Logger, term: Terminal) -> Result<(), TracerError> {
window.limit_update_rate(Some(std::time::Duration::from_micros(16600)));
window
})
- .and_then(|mut window| {
+ .map(|mut window| {
let mut t = Instant::now();
- while window.is_open() && !window.is_key_down(Key::Escape) && !exit.status() {
- let dt = t.elapsed().as_micros() as f64 / 1000000.0;
+ while window_res.is_ok()
+ && window.is_open()
+ && !window.is_key_down(Key::Escape)
+ && !exit.status()
+ {
+ let dt = t.elapsed().as_micros() as f64;
t = Instant::now();
- key_inputs.update(&window, dt);
- // Sleep a bit to not hog the lock on the buffer all the time.
- std::thread::sleep(std::time::Duration::from_millis(1));
-
- screen_buffer
- .read()
- .map_err(|e| TracerError::FailedToUpdateWindow(e.to_string()))
- .and_then(|buf| {
- window
- .update_with_buffer(&buf, image.width, image.height)
- .map_err(|e| TracerError::FailedToUpdateWindow(e.to_string()))
- })?
+ inputs.update(&window, dt);
+
+ window_res =
+ scene_controller
+ .get_buffer()
+ .and_then(|maybe_buf| match maybe_buf {
+ Some(buf) => window
+ .update_with_buffer(&buf, image.width, image.height)
+ .map_err(|e| TracerError::FailedToUpdateWindow(e.to_string())),
+ None => {
+ window.update();
+ Ok(())
+ }
+ });
}
exit.signal();
- render_image.signal();
- Ok(())
+ scene_controller.stop()
});
});
});
@@ -265,7 +186,7 @@ fn main() {
Err(TracerError::ExitEvent) => {}
Ok(_) => {}
Err(e) => {
- error!(log, "{}", e);
+ error!(log, "Error: {}", e);
let exit_code = i32::from(e);
error!(log, "Exiting with: {}", exit_code);
std::process::exit(exit_code)
diff --git a/racer-tracer/src/renderer.rs b/racer-tracer/src/renderer.rs
index 162dc4b..0c351c9 100644
--- a/racer-tracer/src/renderer.rs
+++ b/racer-tracer/src/renderer.rs
@@ -3,10 +3,18 @@ use std::{sync::RwLock, time::Duration};
use synchronoise::SignalEvent;
use crate::{
- camera::Camera, config::Config, error::TracerError, geometry::Hittable, image::Image, ray::Ray,
- vec3::Color, vec3::Vec3,
+ camera::Camera,
+ config::{Config, Renderer as ConfigRenderer},
+ error::TracerError,
+ geometry::Hittable,
+ image::Image,
+ ray::Ray,
+ vec3::Color,
+ vec3::Vec3,
};
+use self::{cpu::CpuRenderer, cpu_scaled::CpuRendererScaled};
+
pub mod cpu;
pub mod cpu_scaled;
@@ -44,8 +52,18 @@ pub struct RenderData<'a> {
pub scene: &'a dyn Hittable,
pub config: &'a Config,
pub cancel_event: Option<&'a SignalEvent>,
+ pub buffer_updated: Option<&'a SignalEvent>,
}
pub trait Renderer: Send + Sync {
fn render(&self, render_data: RenderData) -> Result<(), TracerError>;
}
+
+impl From<&ConfigRenderer> for &dyn Renderer {
+ fn from(r: &ConfigRenderer) -> Self {
+ match r {
+ ConfigRenderer::Cpu => &CpuRenderer {} as &dyn Renderer,
+ ConfigRenderer::CpuPreview => &CpuRendererScaled {} as &dyn Renderer,
+ }
+ }
+}
diff --git a/racer-tracer/src/renderer/cpu.rs b/racer-tracer/src/renderer/cpu.rs
index f97ad9c..48e9528 100644
--- a/racer-tracer/src/renderer/cpu.rs
+++ b/racer-tracer/src/renderer/cpu.rs
@@ -59,6 +59,9 @@ impl CpuRenderer {
buf[offset + row * image.screen_width + col] = color;
}
}
+ if let Some(updated) = rd.buffer_updated {
+ updated.signal()
+ }
})
}
diff --git a/racer-tracer/src/renderer/cpu_scaled.rs b/racer-tracer/src/renderer/cpu_scaled.rs
index 44e73fa..3cbbaab 100644
--- a/racer-tracer/src/renderer/cpu_scaled.rs
+++ b/racer-tracer/src/renderer/cpu_scaled.rs
@@ -81,6 +81,9 @@ impl CpuRendererScaled {
}
}
}
+ if let Some(updated) = rd.buffer_updated {
+ updated.signal()
+ }
})
}
}
diff --git a/racer-tracer/src/scene_controller.rs b/racer-tracer/src/scene_controller.rs
new file mode 100644
index 0000000..4a867f5
--- /dev/null
+++ b/racer-tracer/src/scene_controller.rs
@@ -0,0 +1,37 @@
+pub mod interactive;
+
+use slog::Logger;
+
+use crate::{
+ camera::Camera, config::Config, error::TracerError, image::Image, key_inputs::KeyCallback,
+ scene::Scene, terminal::Terminal,
+};
+
+pub fn create_screen_buffer(image: &Image) -> Vec<u32> {
+ vec![0; image.width * image.height]
+}
+
+pub struct SceneData {
+ pub log: Logger,
+ pub term: Terminal,
+ pub config: Config,
+ pub scene: Scene,
+ pub camera: Camera,
+ pub image: Image,
+}
+
+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<KeyCallback>;
+
+ // Render function
+ fn render(&self) -> Result<(), TracerError>;
+
+ // Returns the screen buffer produced by the scene controller.
+ // Returns None if no new buffer is available
+ fn get_buffer(&self) -> Result<Option<Vec<u32>>, TracerError>;
+
+ // Called when the application wants to exit.
+ fn stop(&self);
+}
diff --git a/racer-tracer/src/scene_controller/interactive.rs b/racer-tracer/src/scene_controller/interactive.rs
new file mode 100644
index 0000000..4624ba1
--- /dev/null
+++ b/racer-tracer/src/scene_controller/interactive.rs
@@ -0,0 +1,209 @@
+use std::{
+ sync::RwLock,
+ time::{Duration, Instant},
+};
+
+use minifb::Key;
+use slog::Logger;
+use synchronoise::SignalEvent;
+
+use crate::{
+ camera::Camera,
+ config::Config,
+ error::TracerError,
+ image::Image,
+ image_action::ImageAction,
+ key_inputs::{KeyCallback, KeyEvent, KeyInputs},
+ renderer::{RenderData, Renderer},
+ scene::Scene,
+ terminal::Terminal,
+};
+
+use super::{create_screen_buffer, SceneController, SceneData};
+
+pub struct InteractiveScene<'renderer, 'action> {
+ screen_buffer: RwLock<Vec<u32>>,
+ preview_buffer: RwLock<Vec<u32>>,
+ camera_speed: f64,
+ render_image_event: SignalEvent,
+ buffer_updated: SignalEvent,
+ stop_event: SignalEvent,
+
+ log: Logger,
+ term: Terminal,
+ image_action: &'action dyn ImageAction,
+ config: Config,
+ scene: Scene,
+ camera: RwLock<Camera>,
+ image: Image,
+ renderer: &'renderer dyn Renderer,
+ renderer_preview: &'renderer dyn Renderer,
+}
+
+impl<'renderer, 'action> InteractiveScene<'renderer, 'action> {
+ pub fn new(scene_data: SceneData, camera_speed: f64) -> Self {
+ Self {
+ screen_buffer: RwLock::new(create_screen_buffer(&scene_data.image)),
+ preview_buffer: RwLock::new(create_screen_buffer(&scene_data.image)),
+ camera_speed,
+ render_image_event: SignalEvent::manual(false),
+ buffer_updated: SignalEvent::manual(false),
+ stop_event: SignalEvent::manual(false),
+
+ log: scene_data.log,
+ term: scene_data.term,
+ image_action: (&scene_data.config.image_action).into(),
+ scene: scene_data.scene,
+ camera: RwLock::new(scene_data.camera),
+ image: scene_data.image,
+ renderer: (&scene_data.config.renderer).into(),
+ renderer_preview: (&scene_data.config.preview_renderer).into(),
+ config: scene_data.config,
+ }
+ }
+}
+
+impl<'renderer, 'action> SceneController for InteractiveScene<'renderer, 'action> {
+ fn get_inputs(&self) -> Vec<KeyCallback> {
+ vec![
+ KeyInputs::input(KeyEvent::Release, Key::R, |_| {
+ self.render_image_event.signal();
+ Ok(())
+ }),
+ KeyInputs::input(KeyEvent::Down, Key::W, |dt| {
+ self.camera
+ .write()
+ .map_err(|e| TracerError::KeyError(e.to_string()))
+ .map(|mut cam| {
+ cam.go_forward(-dt * self.camera_speed);
+ })
+ }),
+ KeyInputs::input(KeyEvent::Down, Key::S, |dt| {
+ self.camera
+ .write()
+ .map_err(|e| TracerError::KeyError(e.to_string()))
+ .map(|mut cam| {
+ cam.go_forward(dt * self.camera_speed);
+ })
+ }),
+ KeyInputs::input(KeyEvent::Down, Key::A, |dt| {
+ self.camera
+ .write()
+ .map_err(|e| TracerError::KeyError(e.to_string()))
+ .map(|mut cam| {
+ cam.go_right(-dt * self.camera_speed);
+ })
+ }),
+ KeyInputs::input(KeyEvent::Down, Key::D, |dt| {
+ self.camera
+ .write()
+ .map_err(|e| TracerError::KeyError(e.to_string()))
+ .map(|mut cam| {
+ cam.go_right(dt * self.camera_speed);
+ })
+ }),
+ ]
+ }
+
+ fn get_buffer(&self) -> Result<Option<Vec<u32>>, TracerError> {
+ self.buffer_updated
+ .wait_timeout(Duration::from_secs(0))
+ .then_some(|| ())
+ .map_or(Ok(None), |_| {
+ self.screen_buffer
+ .read()
+ .map_err(|e| TracerError::FailedToAcquireLock(e.to_string()))
+ .map(|v| Some(v.to_owned()))
+ })
+ }
+
+ fn render(&self) -> Result<(), TracerError> {
+ self.render_image_event
+ .wait_timeout(Duration::from_secs(0))
+ .then_some(|| ())
+ .map_or_else(
+ || {
+ // Render preview
+ self.render_image_event.reset();
+
+ // We do not want partial screen updates for the preview.
+ // Which is why we send in a different buffer.
+ self.renderer_preview
+ .render(RenderData {
+ buffer: &self.preview_buffer,
+ camera: &self.camera,
+ image: &self.image,
+ scene: &self.scene,
+ config: &self.config,
+ cancel_event: None,
+ buffer_updated: None,
+ })
+ .and_then(|_| {
+ self.screen_buffer
+ .write()
+ .map_err(|e| TracerError::FailedToAcquireLock(e.to_string()))
+ })
+ .and_then(|w_buffer| {
+ self.preview_buffer
+ .read()
+ .map_err(|e| TracerError::FailedToAcquireLock(e.to_string()))
+ .map(|r_buffer| (r_buffer, w_buffer))
+ })
+ .map(|(r_buffer, mut w_buffer)| {
+ // For the preview we want the complete image
+ // result before signaling the image is done.
+ *w_buffer = r_buffer.to_owned();
+ self.buffer_updated.signal();
+ })
+ },
+ |_| {
+ let render_time = Instant::now();
+ self.render_image_event.reset();
+
+ // When we render the final image we want partial
+ // updates to the screen buffer. We send in our
+ // original screen_buffer and the buffer_updated
+ // signal. This will ensure that the window will
+ // get updated with a new buffer as soon as a
+ // thread finishes writing a block and we get
+ // partial updates of the rendered image.
+ self.renderer
+ .render(RenderData {
+ buffer: &self.screen_buffer,
+ camera: &self.camera,
+ image: &self.image,
+ scene: &self.scene,
+ config: &self.config,
+ cancel_event: Some(&self.render_image_event),
+ buffer_updated: Some(&self.buffer_updated),
+ })
+ .and_then(|_| {
+ if !self.render_image_event.status() {
+ info!(
+ self.log,
+ "It took {} seconds to render the image.",
+ Instant::now().duration_since(render_time).as_secs()
+ );
+ } else {
+ info!(self.log, "Image render cancelled.");
+ }
+
+ self.image_action.action(
+ &self.screen_buffer,
+ &self.stop_event,
+ &self.render_image_event,
+ &self.config,
+ self.log.new(o!("scope" => "image-action")),
+ &self.term,
+ )
+ })
+ },
+ )
+ }
+
+ fn stop(&self) {
+ // If we are currently rendering anything we try to cancel it
+ self.render_image_event.signal();
+ self.stop_event.signal();
+ }
+}