use std::fs::File;
use std::sync::mpsc;
use all_is_cubes::camera::{Flaws, StandardCameras};
use all_is_cubes::listen::ListenableSource;
use all_is_cubes::raytracer::RtRenderer;
use all_is_cubes_port::gltf::{GltfDataDestination, GltfWriter};
mod options;
pub(crate) use options::*;
mod record_main;
pub(crate) use record_main::{create_recording_session, record_main};
mod write_gltf;
mod write_png;
type FrameNumber = usize;
#[derive(Debug)]
pub(crate) struct Recorder {
sending_frame_number: FrameNumber,
inner: RecorderInner,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
enum RecorderInner {
Shutdown,
Raytrace(RtRecorder),
Mesh(write_gltf::MeshRecorder),
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct Status {
pub frame_number: FrameNumber,
pub flaws: Flaws,
}
impl Recorder {
fn new(
options: RecordOptions,
cameras: StandardCameras,
) -> Result<(Self, mpsc::Receiver<Status>), anyhow::Error> {
let (mut status_sender, status_receiver) = mpsc::channel::<Status>();
let inner = match options.output_format {
RecordFormat::PngOrApng => {
let (scene_sender, scene_receiver) =
mpsc::sync_channel::<(FrameNumber, RtRenderer)>(1);
let (image_data_sender, image_data_receiver) = mpsc::sync_channel(1);
std::thread::Builder::new()
.name("renderer".to_string())
.spawn({
move || {
while let Ok((frame_number, renderer)) = scene_receiver.recv() {
let (image, _info, flaws) = renderer.draw_rgba(|_| String::new());
image_data_sender
.send((
Status {
frame_number,
flaws,
},
image,
))
.unwrap();
}
}
})?;
std::thread::Builder::new()
.name("image encoder".to_string())
.spawn({
let file = File::create(&options.output_path)?;
move || {
write_png::threaded_write_frames(
file,
options,
image_data_receiver,
&mut status_sender,
)
.expect("writing PNG file failed");
}
})?;
RecorderInner::Raytrace(RtRecorder {
cameras,
scene_sender,
})
}
RecordFormat::Gltf => {
let (scene_sender, scene_receiver) =
mpsc::sync_channel::<write_gltf::MeshRecordMsg>(1);
let writer = GltfWriter::new(GltfDataDestination::new(
Some(options.output_path.clone()),
2000,
));
let tex = writer.texture_allocator();
write_gltf::start_gltf_writing(&options, writer, scene_receiver, status_sender)?;
RecorderInner::Mesh(write_gltf::MeshRecorder::new(cameras, tex, scene_sender))
}
};
Ok((
Recorder {
inner,
sending_frame_number: 0,
},
status_receiver,
))
}
pub fn capture_frame(&mut self) {
let this_frame_number = self.sending_frame_number;
self.sending_frame_number += 1;
match &mut self.inner {
RecorderInner::Raytrace(rec) => {
let mut renderer = RtRenderer::new(
rec.cameras.clone(),
Box::new(|v| v),
ListenableSource::constant(()),
);
renderer.update(None).unwrap();
rec.scene_sender
.send((this_frame_number, renderer))
.expect("channel closed; recorder render thread died?");
}
RecorderInner::Mesh(rec) => rec.capture_frame(this_frame_number),
RecorderInner::Shutdown => unreachable!(),
}
}
pub fn no_more_frames(&mut self) {
self.inner = RecorderInner::Shutdown;
}
}
#[derive(Debug)]
pub(crate) struct RtRecorder {
cameras: StandardCameras,
scene_sender: mpsc::SyncSender<(FrameNumber, RtRenderer)>,
}