use std::path::PathBuf; use crate::raytracer::{ scene::Scene, sphere::TextureSphere, texture::TextureWrapper, types::{Light, Point2}, }; use lispers_macro::{native_lisp_function, native_lisp_function_proxy}; use lispers_core::lisp::{ environment::EnvironmentLayer, eval::{eval, EvalError}, expression::ForeignDataWrapper, Environment, Expression, }; use super::{ camera::Camera, plane::{Checkerboard, Plane, TexturePlane}, sphere::Sphere, texture::MandelbrotTexture, types::{Color, Material, Point3, RTObjectWrapper, Vector3}, RTError, }; #[native_lisp_function(eval)] pub fn point(x: f64, y: f64, z: f64) -> Result, EvalError> { Ok(ForeignDataWrapper::new(Point3::new(x, y, z))) } #[native_lisp_function(eval)] pub fn point2(x: f64, y: f64) -> Result, EvalError> { Ok(ForeignDataWrapper::new(Point2::new(x, y))) } #[native_lisp_function(eval)] pub fn vector(x: f64, y: f64, z: f64) -> Result, EvalError> { Ok(ForeignDataWrapper::new(Vector3::new(x, y, z))) } #[native_lisp_function(eval)] pub fn color(r: f64, g: f64, b: f64) -> Result, EvalError> { Ok(ForeignDataWrapper::new(Color::new(r, g, b))) } #[native_lisp_function(eval)] pub fn light( pos: ForeignDataWrapper, col: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(Light::new(*pos, *col))) } #[native_lisp_function(eval)] pub fn material( amb: ForeignDataWrapper, dif: ForeignDataWrapper, spe: ForeignDataWrapper, shi: f64, mir: f64, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(Material::new( *amb, *dif, *spe, shi, mir, ))) } #[native_lisp_function(eval)] pub fn sphere( pos: ForeignDataWrapper, rad: f64, mat: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Sphere::new(*pos, rad, *mat))).into()) } #[native_lisp_function(eval)] pub fn texture_sphere( pos: ForeignDataWrapper, rad: f64, tex: ForeignDataWrapper, ) -> Result, EvalError> { Ok( ForeignDataWrapper::new(RTObjectWrapper::from(TextureSphere::new( *pos, rad, tex.clone(), ))) .into(), ) } #[native_lisp_function(eval)] pub fn plane( pos: ForeignDataWrapper, dir: ForeignDataWrapper, mat: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Plane::new(*pos, *dir, *mat))).into()) } #[native_lisp_function(eval)] pub fn checkerboard( pos: ForeignDataWrapper, norm: ForeignDataWrapper, mat1: ForeignDataWrapper, mat2: ForeignDataWrapper, sca: f64, up: ForeignDataWrapper, ) -> Result, EvalError> { Ok( ForeignDataWrapper::new(RTObjectWrapper::from(Checkerboard::new( *pos, *norm, *mat1, *mat2, sca, *up, ))) .into(), ) } #[native_lisp_function(eval)] pub fn texture_plane( texture: ForeignDataWrapper, pos: ForeignDataWrapper, norm: ForeignDataWrapper, sca: f64, up: ForeignDataWrapper, ) -> Result, EvalError> { Ok( ForeignDataWrapper::new(RTObjectWrapper::from(TexturePlane::new( *pos, *norm, texture.clone(), sca, *up, ))) .into(), ) } #[native_lisp_function(eval)] pub fn mandelbrot_texture( scale: f64, at: ForeignDataWrapper, max_iter: i64, ambient_color: ForeignDataWrapper, diffuse_color: ForeignDataWrapper, specular_color: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(TextureWrapper::new( MandelbrotTexture::new( scale, *at, max_iter as u32, *ambient_color, *diffuse_color, *specular_color, ), ))) } pub fn scene(env: &Environment, expr: Expression) -> Result { let [amb, objs, lgts]: [Expression; 3] = expr.try_into()?; let amb: ForeignDataWrapper = eval(env, amb)?.try_into()?; let objs: Vec = eval(env, objs)?.try_into()?; let lgts: Vec = eval(env, lgts)?.try_into()?; let mut scene = Scene::new(); scene.set_ambient(*amb); for o in objs { let o: ForeignDataWrapper = eval(env, o)?.try_into()?; scene.add_object(o.clone()); } for l in lgts { let l: ForeignDataWrapper = eval(env, l)?.try_into()?; scene.add_light(*l); } Ok(ForeignDataWrapper::new(scene).into()) } #[native_lisp_function] pub fn scene_add_object( mut sce: ForeignDataWrapper, obj: ForeignDataWrapper, ) -> Result, EvalError> { sce.add_object(obj.clone()); Ok(sce) } #[native_lisp_function] pub fn scene_add_light( mut sce: ForeignDataWrapper, lgt: ForeignDataWrapper, ) -> Result, EvalError> { sce.add_light(*lgt); Ok(sce) } native_lisp_function_proxy!( fname = scene_add, eval, dispatch = scene_add_object, dispatch = scene_add_light ); #[native_lisp_function(eval)] pub fn camera( pos: ForeignDataWrapper, cnt: ForeignDataWrapper, up: ForeignDataWrapper, fovy: f64, w: i64, h: i64, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(Camera::new( *pos, *cnt, *up, fovy, w as usize, h as usize, ))) } #[native_lisp_function(eval)] pub fn camera_reposition( cam: ForeignDataWrapper, pos: ForeignDataWrapper, cnt: ForeignDataWrapper, up: ForeignDataWrapper, fovy: f64, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new( cam.to_owned().reposition(*pos, *cnt, *up, fovy), )) } #[native_lisp_function(eval)] pub fn render( cam: ForeignDataWrapper, sce: ForeignDataWrapper, dpt: i64, sbp: i64, out: String, ) -> Result { println!("Rendering to {}...", out); let img = cam.render(&sce, dpt as u32, sbp as u32); match img.save(out) { Ok(_) => Ok(Expression::Nil), Err(e) => Err(EvalError::RuntimeError(e.to_string())), } } pub fn render_animation(env: &Environment, expr: Expression) -> Result { let [cam, path, scene_fn, update_cam, frames, fps, depth, subp]: [Expression; 8] = expr.try_into()?; let cam: ForeignDataWrapper = eval(env, cam)?.try_into()?; let path: String = eval(env, path)?.try_into()?; let frames: i64 = eval(env, frames)?.try_into()?; let fps: i64 = eval(env, fps)?.try_into()?; let depth: i64 = eval(env, depth)?.try_into()?; let subp: i64 = eval(env, subp)?.try_into()?; let sfn = |t: u32| -> Result { let scene_fn_call: Expression = [scene_fn.clone(), (t as i64).into()].into(); let scn: ForeignDataWrapper = eval(env, scene_fn_call)?.try_into()?; Ok(scn.to_owned()) }; let ucm = |t: u32, c: &Camera| -> Result { let c = ForeignDataWrapper::new(c.to_owned()); let update_cam_call: Expression = [update_cam.clone(), (t as i64).into(), c.into()].into(); let new_c: ForeignDataWrapper = eval(env, update_cam_call)?.try_into()?; Ok(new_c.to_owned()) }; let path: PathBuf = path.into(); match cam.render_animation( &path, sfn, ucm, frames as u32, fps as u32, depth as u32, subp as u32, ) { Ok(()) => Ok(Expression::Nil), Err(RTError::EvalError(e)) => Err(e), Err(RTError::FFMpegError(e)) => Err(EvalError::RuntimeError(e.to_string())), } } #[native_lisp_function(eval)] pub fn sin(x: f64) -> Result { Ok(x.sin()) } #[native_lisp_function(eval)] pub fn cos(x: f64) -> Result { Ok(x.cos()) } #[native_lisp_function(eval)] pub fn add_i(x: i64, y: i64) -> Result { Ok(x + y) } #[native_lisp_function(eval)] pub fn add_f(x: f64, y: f64) -> Result { Ok(x + y) } #[native_lisp_function] pub fn vadd_vv( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a + *b)) } #[native_lisp_function] pub fn vadd_vp( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*b + *a)) } #[native_lisp_function] pub fn vadd_pv( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a + *b)) } native_lisp_function_proxy!( fname = add, eval, dispatch = add_i, dispatch = add_f, dispatch = vadd_vv, dispatch = vadd_vp, dispatch = vadd_pv ); #[native_lisp_function(eval)] pub fn sub_i(x: i64, y: i64) -> Result { Ok(x - y) } #[native_lisp_function(eval)] pub fn sub_f(x: f64, y: f64) -> Result { Ok(x - y) } #[native_lisp_function] pub fn sub_vv( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a - *b)) } #[native_lisp_function] pub fn sub_vp( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*b - *a)) } #[native_lisp_function] pub fn sub_pv( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a - *b)) } #[native_lisp_function] pub fn sub_pp( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a - *b)) } native_lisp_function_proxy!( fname = sub, eval, dispatch = sub_i, dispatch = sub_f, dispatch = sub_vv, dispatch = sub_vp, dispatch = sub_pv, dispatch = sub_pp ); #[native_lisp_function(eval)] pub fn mul_i(x: i64, y: i64) -> Result { Ok(x * y) } #[native_lisp_function(eval)] pub fn mul_f(x: f64, y: f64) -> Result { Ok(x * y) } #[native_lisp_function] pub fn mul_vs( a: ForeignDataWrapper, b: f64, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a * b)) } #[native_lisp_function] pub fn mul_sv( a: f64, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*b * a)) } #[native_lisp_function] pub fn mul_ps( a: ForeignDataWrapper, b: f64, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a * b)) } #[native_lisp_function] pub fn mul_sp( a: f64, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*b * a)) } native_lisp_function_proxy!( fname = mul, eval, dispatch = mul_i, dispatch = mul_f, dispatch = mul_vs, dispatch = mul_sv, dispatch = mul_ps, dispatch = mul_sp ); #[native_lisp_function(eval)] pub fn div_i(x: i64, y: i64) -> Result { Ok(x as f64 / y as f64) } #[native_lisp_function(eval)] pub fn div_f(x: f64, y: f64) -> Result { Ok(x / y) } #[native_lisp_function] pub fn div_vs( a: ForeignDataWrapper, b: f64, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a / b)) } #[native_lisp_function] pub fn div_sv( a: f64, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*b / a)) } #[native_lisp_function] pub fn div_ps( a: ForeignDataWrapper, b: f64, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*a / b)) } #[native_lisp_function] pub fn div_sp( a: f64, b: ForeignDataWrapper, ) -> Result, EvalError> { Ok(ForeignDataWrapper::new(*b / a)) } native_lisp_function_proxy!( fname = div, eval, dispatch = div_i, dispatch = div_f, dispatch = div_vs, dispatch = div_sv, dispatch = div_ps, dispatch = div_sp ); #[native_lisp_function(eval)] pub fn dot( a: ForeignDataWrapper, b: ForeignDataWrapper, ) -> Result { Ok(a.dot(&b)) } #[native_lisp_function] pub fn abs_i(a: i64) -> Result { Ok(a.abs()) } #[native_lisp_function] pub fn abs_f(a: f64) -> Result { Ok(a.abs()) } #[native_lisp_function] pub fn abs_v(a: ForeignDataWrapper) -> Result { Ok(a.dot(&a).sqrt()) } native_lisp_function_proxy!( fname = abs, eval, dispatch = abs_i, dispatch = abs_f, dispatch = abs_v ); /// Adds the raytracing functions to the given environment layer. pub fn mk_raytrace(layer: &mut EnvironmentLayer) { layer.set("point".to_string(), Expression::Function(point)); layer.set("point2".to_string(), Expression::Function(point2)); layer.set("vector".to_string(), Expression::Function(vector)); layer.set("color".to_string(), Expression::Function(color)); layer.set("light".to_string(), Expression::Function(light)); layer.set("material".to_string(), Expression::Function(material)); layer.set("plane".to_string(), Expression::Function(plane)); layer.set( "checkerboard".to_string(), Expression::Function(checkerboard), ); layer.set( "texture-plane".to_string(), Expression::Function(texture_plane), ); layer.set( "mandelbrot-texture".to_string(), Expression::Function(mandelbrot_texture), ); layer.set("sphere".to_string(), Expression::Function(sphere)); layer.set( "texture-sphere".to_string(), Expression::Function(texture_sphere), ); layer.set("scene".to_string(), Expression::Function(scene)); layer.set("scene-add".to_string(), Expression::Function(scene_add)); layer.set("camera".to_string(), Expression::Function(camera)); layer.set( "camera-reposition".to_string(), Expression::Function(camera_reposition), ); layer.set("render".to_string(), Expression::Function(render)); layer.set( "render-animation".to_string(), Expression::Function(render_animation), ); layer.set("sin".to_string(), Expression::Function(sin)); layer.set("cos".to_string(), Expression::Function(cos)); layer.set("+".to_string(), Expression::Function(add)); layer.set("-".to_string(), Expression::Function(sub)); layer.set("*".to_string(), Expression::Function(mul)); layer.set("/".to_string(), Expression::Function(div)); layer.set("dot".to_string(), Expression::Function(dot)); layer.set("abs".to_string(), Expression::Function(abs)); }