diff options
Diffstat (limited to 'racer-tracer/src/scene_controller/interactive.rs')
| -rw-r--r-- | racer-tracer/src/scene_controller/interactive.rs | 209 |
1 files changed, 209 insertions, 0 deletions
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(); + } +} |
