Compare commits
2 Commits
d4281d3538
...
fc40e0b798
| Author | SHA1 | Date | |
|---|---|---|---|
|
fc40e0b798
|
|||
|
d0840759b3
|
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -894,6 +894,7 @@ dependencies = [
|
|||||||
"lispers-core",
|
"lispers-core",
|
||||||
"lispers-macro",
|
"lispers-macro",
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
|
"ndarray",
|
||||||
"nix",
|
"nix",
|
||||||
"rayon",
|
"rayon",
|
||||||
"video-rs",
|
"video-rs",
|
||||||
@@ -1033,6 +1034,21 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ndarray"
|
||||||
|
version = "0.17.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d"
|
||||||
|
dependencies = [
|
||||||
|
"matrixmultiply",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"portable-atomic",
|
||||||
|
"portable-atomic-util",
|
||||||
|
"rawpointer",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "new_debug_unreachable"
|
name = "new_debug_unreachable"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -1194,6 +1210,21 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic-util"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potential_utf"
|
name = "potential_utf"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -1669,6 +1700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2633ec4c2a8aeb7c0e970f75ba99122a75841e9f7b34d5225366d0e61a870a8c"
|
checksum = "2633ec4c2a8aeb7c0e970f75ba99122a75841e9f7b34d5225366d0e61a870a8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ffmpeg-next",
|
"ffmpeg-next",
|
||||||
|
"ndarray",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -43,4 +43,5 @@ nix = "0.31.2"
|
|||||||
rayon = "1.11.0"
|
rayon = "1.11.0"
|
||||||
lispers-core = {workspace = true}
|
lispers-core = {workspace = true}
|
||||||
lispers-macro = {workspace = true}
|
lispers-macro = {workspace = true}
|
||||||
video-rs = "0.11.0"
|
video-rs = { version = "0.11.0", features = ["ndarray"] }
|
||||||
|
ndarray = "0.17.2"
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::parser::ParserError;
|
||||||
|
|
||||||
use super::environment::Environment;
|
use super::environment::Environment;
|
||||||
use super::environment::EnvironmentLayer;
|
use super::environment::EnvironmentLayer;
|
||||||
use super::expression::Expression;
|
use super::expression::Expression;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
/// All possible evaluation errors
|
/// All possible evaluation errors
|
||||||
pub enum EvalError {
|
pub enum EvalError {
|
||||||
SymbolNotBound(String),
|
SymbolNotBound(String),
|
||||||
@@ -14,6 +16,7 @@ pub enum EvalError {
|
|||||||
TypeError(String),
|
TypeError(String),
|
||||||
NotASymbol(Expression),
|
NotASymbol(Expression),
|
||||||
RuntimeError(String),
|
RuntimeError(String),
|
||||||
|
ParserError(ParserError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for EvalError {
|
impl Display for EvalError {
|
||||||
@@ -26,6 +29,7 @@ impl Display for EvalError {
|
|||||||
EvalError::TypeError(s) => write!(f, "Type error: {}", s),
|
EvalError::TypeError(s) => write!(f, "Type error: {}", s),
|
||||||
EvalError::NotASymbol(e) => write!(f, "Expression {} is not a symbol", e),
|
EvalError::NotASymbol(e) => write!(f, "Expression {} is not a symbol", e),
|
||||||
EvalError::RuntimeError(s) => write!(f, "Runtime error: {}", s),
|
EvalError::RuntimeError(s) => write!(f, "Runtime error: {}", s),
|
||||||
|
EvalError::ParserError(s) => write!(f, "Parser error: {}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use super::tokenizer::tokenize;
|
|||||||
use super::tokenizer::TokenStream;
|
use super::tokenizer::TokenStream;
|
||||||
use super::tokenizer::TokenizerError;
|
use super::tokenizer::TokenizerError;
|
||||||
use crate::lisp::Expression;
|
use crate::lisp::Expression;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -18,6 +19,16 @@ impl From<TokenizerError> for ParserError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for ParserError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ParserError::TokenizerError(t) => write!(f, "Tokenizer Error: {}", t),
|
||||||
|
ParserError::UnexpectedToken(t) => write!(f, "Unexpecte Token: {}", t),
|
||||||
|
ParserError::UnexpectedEndOfInput => write!(f, "Unexpected end of input."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_list<I>(stream: &mut Peekable<TokenStream<I>>) -> Result<Expression, ParserError>
|
fn parse_list<I>(stream: &mut Peekable<TokenStream<I>>) -> Result<Expression, ParserError>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = char>,
|
I: Iterator<Item = char>,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
/// Sum type of different tokens
|
/// Sum type of different tokens
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
@@ -12,3 +14,20 @@ pub enum Token {
|
|||||||
Symbol(String),
|
Symbol(String),
|
||||||
True,
|
True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Token {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Token::FloatLiteral(x) => write!(f, "{}", x),
|
||||||
|
Token::IntLiteral(x) => write!(f, "{}", x),
|
||||||
|
Token::Dot => write!(f, "."),
|
||||||
|
Token::Nil => write!(f, "nil"),
|
||||||
|
Token::ParClose => write!(f, ")"),
|
||||||
|
Token::ParOpen => write!(f, "("),
|
||||||
|
Token::Quote => write!(f, "'"),
|
||||||
|
Token::StringLiteral(x) => write!(f, "\"{}\"", x),
|
||||||
|
Token::Symbol(x) => write!(f, "{}", x),
|
||||||
|
Token::True => write!(f, "true"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use super::token::Token;
|
use super::token::Token;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -7,6 +9,14 @@ pub enum TokenizerError {
|
|||||||
UnmatchedSequence(String),
|
UnmatchedSequence(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for TokenizerError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TokenizerError::UnmatchedSequence(s) => write!(f, "Unmatched sequence: {}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A reader used to wrap the `TokenStream`.
|
/// A reader used to wrap the `TokenStream`.
|
||||||
/// When reading, it starts with the staging buffer of the stream, once
|
/// When reading, it starts with the staging buffer of the stream, once
|
||||||
/// it's end is reached, the input stream is copied character wise to
|
/// it's end is reached, the input stream is copied character wise to
|
||||||
|
|||||||
@@ -80,15 +80,15 @@
|
|||||||
(defun cam-fn (t c)
|
(defun cam-fn (t c)
|
||||||
(let '((pos . (point 0 4 8))
|
(let '((pos . (point 0 4 8))
|
||||||
(cnt . (point 0 0 0))
|
(cnt . (point 0 0 0))
|
||||||
(to . (point -3 3 -8))
|
(to . (point -2 3 -6))
|
||||||
(up . (vector 0 1 0))
|
(up . (vector 0 1 0))
|
||||||
(fovy . 40)
|
(fovy . 40)
|
||||||
(pct . (/ t 300.0)))
|
(pct . (/ t 300.0)))
|
||||||
(let '((tpos . (vadd pos (vmul (vsub to pos) pct)))
|
(let '((tpos . (vadd pos (vmul (vsub to pos) pct)))
|
||||||
(tfovy . (+ fovy (* 40 pct)))
|
(tfovy . (+ fovy (* 40 pct)))
|
||||||
)
|
)
|
||||||
(camera-reposition c tpos cnt up fovy)
|
(camera-reposition c tpos cnt up tfovy)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
|
||||||
(render-animation cam scene-fn cam-fn 300 1 4 2)
|
(render-animation cam "demo-animation-2.mp4" scene-fn cam-fn 300 30 4 2)
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
use std::fmt::Display;
|
use std::{fmt::Display, path::Path};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
scene::Scene,
|
scene::Scene,
|
||||||
types::{Color, Point3, Ray, Scalar, Vector3},
|
types::{Color, Point3, Ray, Scalar, Vector3},
|
||||||
|
RTError,
|
||||||
};
|
};
|
||||||
use image::RgbImage;
|
use image::RgbImage;
|
||||||
|
use lispers_core::lisp::eval::EvalError;
|
||||||
|
use ndarray::Array3;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
use video_rs::{encode::Settings, Encoder, Time};
|
||||||
|
|
||||||
/// A camera that can render a scene.
|
/// A camera that can render a scene.
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
@@ -119,26 +123,47 @@ impl Camera {
|
|||||||
Camera::new(position, center, up, fovy, self.width, self.height)
|
Camera::new(position, center, up, fovy, self.width, self.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_animation<SFn: Fn(u32) -> Scene, CFn: Fn(u32, &Camera) -> Camera>(
|
pub fn render_animation<
|
||||||
|
SFn: Fn(u32) -> Result<Scene, EvalError>,
|
||||||
|
CFn: Fn(u32, &Camera) -> Result<Camera, EvalError>,
|
||||||
|
>(
|
||||||
&self,
|
&self,
|
||||||
|
path: &Path,
|
||||||
scene_fn: SFn,
|
scene_fn: SFn,
|
||||||
update_cam: CFn,
|
update_cam: CFn,
|
||||||
frames: u32,
|
frames: u32,
|
||||||
fps: u32,
|
fps: u32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
subp: u32,
|
subp: u32,
|
||||||
) {
|
) -> Result<(), RTError> {
|
||||||
|
let mut encoder = Encoder::new(
|
||||||
|
path,
|
||||||
|
Settings::preset_h264_yuv420p(self.width, self.height, false),
|
||||||
|
)?;
|
||||||
|
let frame_duration = Time::from_nth_of_a_second(fps as usize);
|
||||||
|
let mut timestamp = Time::zero();
|
||||||
|
|
||||||
let mut cam = self.to_owned();
|
let mut cam = self.to_owned();
|
||||||
for t in 0..frames {
|
for t in 0..frames {
|
||||||
println!("Rendering frame {}/{}", t + 1, frames);
|
println!(
|
||||||
cam = update_cam(t, &cam);
|
"Rendering frame {}/{} for {}",
|
||||||
let img = cam.render(&scene_fn(t), depth, subp);
|
t + 1,
|
||||||
|
frames,
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
cam = update_cam(t, &cam)?;
|
||||||
|
let img = cam.render(&scene_fn(t)?, depth, subp);
|
||||||
|
|
||||||
match img.save(format!("frame_{:04}.png", t)) {
|
let frame = Array3::from_shape_fn((self.height, self.width, 3), |(y, x, c)| {
|
||||||
Ok(_) => {}
|
img.get_pixel(x as u32, y as u32)[c]
|
||||||
Err(e) => print!("Could not render frame: {}", e),
|
});
|
||||||
}
|
|
||||||
|
encoder.encode(&frame, timestamp)?;
|
||||||
|
timestamp = timestamp.aligned_with(frame_duration).add();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encoder.finish()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::raytracer::{scene::Scene, types::Light};
|
use crate::raytracer::{scene::Scene, types::Light};
|
||||||
|
|
||||||
use lispers_macro::{native_lisp_function, native_lisp_function_proxy};
|
use lispers_macro::{native_lisp_function, native_lisp_function_proxy};
|
||||||
@@ -14,6 +16,7 @@ use super::{
|
|||||||
plane::{Checkerboard, Plane},
|
plane::{Checkerboard, Plane},
|
||||||
sphere::Sphere,
|
sphere::Sphere,
|
||||||
types::{Color, Material, Point3, RTObjectWrapper, Vector3},
|
types::{Color, Material, Point3, RTObjectWrapper, Vector3},
|
||||||
|
RTError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[native_lisp_function(eval)]
|
#[native_lisp_function(eval)]
|
||||||
@@ -179,38 +182,44 @@ pub fn render(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_animation(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
pub fn render_animation(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
|
||||||
let [cam, scene_fn, update_cam, frames, fps, depth, subp]: [Expression; 7] = expr.try_into()?;
|
let [cam, path, scene_fn, update_cam, frames, fps, depth, subp]: [Expression; 8] =
|
||||||
|
expr.try_into()?;
|
||||||
|
|
||||||
let cam: ForeignDataWrapper<Camera> = eval(env, cam)?.try_into()?;
|
let cam: ForeignDataWrapper<Camera> = eval(env, cam)?.try_into()?;
|
||||||
|
let path: String = eval(env, path)?.try_into()?;
|
||||||
let frames: i64 = eval(env, frames)?.try_into()?;
|
let frames: i64 = eval(env, frames)?.try_into()?;
|
||||||
let fps: i64 = eval(env, fps)?.try_into()?;
|
let fps: i64 = eval(env, fps)?.try_into()?;
|
||||||
let depth: i64 = eval(env, depth)?.try_into()?;
|
let depth: i64 = eval(env, depth)?.try_into()?;
|
||||||
let subp: i64 = eval(env, subp)?.try_into()?;
|
let subp: i64 = eval(env, subp)?.try_into()?;
|
||||||
|
|
||||||
let sfn = |t: u32| -> Scene {
|
let sfn = |t: u32| -> Result<Scene, EvalError> {
|
||||||
let scene_fn_call: Expression = [scene_fn.clone(), (t as i64).into()].into();
|
let scene_fn_call: Expression = [scene_fn.clone(), (t as i64).into()].into();
|
||||||
let scn: ForeignDataWrapper<Scene> = eval(env, scene_fn_call).unwrap().try_into().unwrap();
|
let scn: ForeignDataWrapper<Scene> = eval(env, scene_fn_call)?.try_into()?;
|
||||||
scn.to_owned()
|
Ok(scn.to_owned())
|
||||||
};
|
};
|
||||||
|
|
||||||
let ucm = |t: u32, c: &Camera| -> Camera {
|
let ucm = |t: u32, c: &Camera| -> Result<Camera, EvalError> {
|
||||||
let c = ForeignDataWrapper::new(c.to_owned());
|
let c = ForeignDataWrapper::new(c.to_owned());
|
||||||
let update_cam_call: Expression = [update_cam.clone(), (t as i64).into(), c.into()].into();
|
let update_cam_call: Expression = [update_cam.clone(), (t as i64).into(), c.into()].into();
|
||||||
let new_c: ForeignDataWrapper<Camera> =
|
let new_c: ForeignDataWrapper<Camera> = eval(env, update_cam_call)?.try_into()?;
|
||||||
eval(env, update_cam_call).unwrap().try_into().unwrap();
|
Ok(new_c.to_owned())
|
||||||
new_c.to_owned()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
cam.render_animation(
|
let path: PathBuf = path.into();
|
||||||
|
|
||||||
|
match cam.render_animation(
|
||||||
|
&path,
|
||||||
sfn,
|
sfn,
|
||||||
ucm,
|
ucm,
|
||||||
frames as u32,
|
frames as u32,
|
||||||
fps as u32,
|
fps as u32,
|
||||||
depth as u32,
|
depth as u32,
|
||||||
subp as u32,
|
subp as u32,
|
||||||
);
|
) {
|
||||||
|
Ok(()) => Ok(Expression::Nil),
|
||||||
Ok(Expression::Nil)
|
Err(RTError::EvalError(e)) => Err(e),
|
||||||
|
Err(RTError::FFMpegError(e)) => Err(EvalError::RuntimeError(e.to_string())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_lisp_function(eval)]
|
#[native_lisp_function(eval)]
|
||||||
|
|||||||
@@ -5,3 +5,21 @@ pub mod scene;
|
|||||||
pub mod sphere;
|
pub mod sphere;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
mod vec;
|
mod vec;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum RTError {
|
||||||
|
EvalError(lispers_core::lisp::eval::EvalError),
|
||||||
|
FFMpegError(video_rs::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<lispers_core::lisp::eval::EvalError> for RTError {
|
||||||
|
fn from(value: lispers_core::lisp::eval::EvalError) -> Self {
|
||||||
|
RTError::EvalError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<video_rs::Error> for RTError {
|
||||||
|
fn from(value: video_rs::Error) -> Self {
|
||||||
|
RTError::FFMpegError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user