Files
lispers/src/raytracer/lisp.rs

584 lines
15 KiB
Rust

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<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(Point3::new(x, y, z)))
}
#[native_lisp_function(eval)]
pub fn point2(x: f64, y: f64) -> Result<ForeignDataWrapper<Point2>, EvalError> {
Ok(ForeignDataWrapper::new(Point2::new(x, y)))
}
#[native_lisp_function(eval)]
pub fn vector(x: f64, y: f64, z: f64) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(Vector3::new(x, y, z)))
}
#[native_lisp_function(eval)]
pub fn color(r: f64, g: f64, b: f64) -> Result<ForeignDataWrapper<Color>, EvalError> {
Ok(ForeignDataWrapper::new(Color::new(r, g, b)))
}
#[native_lisp_function(eval)]
pub fn light(
pos: ForeignDataWrapper<Point3>,
col: ForeignDataWrapper<Color>,
) -> Result<ForeignDataWrapper<Light>, EvalError> {
Ok(ForeignDataWrapper::new(Light::new(*pos, *col)))
}
#[native_lisp_function(eval)]
pub fn material(
amb: ForeignDataWrapper<Color>,
dif: ForeignDataWrapper<Color>,
spe: ForeignDataWrapper<Color>,
shi: f64,
mir: f64,
) -> Result<ForeignDataWrapper<Material>, EvalError> {
Ok(ForeignDataWrapper::new(Material::new(
*amb, *dif, *spe, shi, mir,
)))
}
#[native_lisp_function(eval)]
pub fn sphere(
pos: ForeignDataWrapper<Point3>,
rad: f64,
mat: ForeignDataWrapper<Material>,
) -> Result<ForeignDataWrapper<RTObjectWrapper>, EvalError> {
Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Sphere::new(*pos, rad, *mat))).into())
}
#[native_lisp_function(eval)]
pub fn texture_sphere(
pos: ForeignDataWrapper<Point3>,
rad: f64,
tex: ForeignDataWrapper<TextureWrapper>,
) -> Result<ForeignDataWrapper<RTObjectWrapper>, EvalError> {
Ok(
ForeignDataWrapper::new(RTObjectWrapper::from(TextureSphere::new(
*pos,
rad,
tex.clone(),
)))
.into(),
)
}
#[native_lisp_function(eval)]
pub fn plane(
pos: ForeignDataWrapper<Point3>,
dir: ForeignDataWrapper<Vector3>,
mat: ForeignDataWrapper<Material>,
) -> Result<ForeignDataWrapper<RTObjectWrapper>, EvalError> {
Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Plane::new(*pos, *dir, *mat))).into())
}
#[native_lisp_function(eval)]
pub fn checkerboard(
pos: ForeignDataWrapper<Point3>,
norm: ForeignDataWrapper<Vector3>,
mat1: ForeignDataWrapper<Material>,
mat2: ForeignDataWrapper<Material>,
sca: f64,
up: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<RTObjectWrapper>, 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<TextureWrapper>,
pos: ForeignDataWrapper<Point3>,
norm: ForeignDataWrapper<Vector3>,
sca: f64,
up: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<RTObjectWrapper>, 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<Point2>,
max_iter: i64,
ambient_color: ForeignDataWrapper<Color>,
diffuse_color: ForeignDataWrapper<Color>,
specular_color: ForeignDataWrapper<Color>,
) -> Result<ForeignDataWrapper<TextureWrapper>, 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<Expression, EvalError> {
let [amb, objs, lgts]: [Expression; 3] = expr.try_into()?;
let amb: ForeignDataWrapper<Color> = eval(env, amb)?.try_into()?;
let objs: Vec<Expression> = eval(env, objs)?.try_into()?;
let lgts: Vec<Expression> = eval(env, lgts)?.try_into()?;
let mut scene = Scene::new();
scene.set_ambient(*amb);
for o in objs {
let o: ForeignDataWrapper<RTObjectWrapper> = eval(env, o)?.try_into()?;
scene.add_object(o.clone());
}
for l in lgts {
let l: ForeignDataWrapper<Light> = 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<Scene>,
obj: ForeignDataWrapper<RTObjectWrapper>,
) -> Result<ForeignDataWrapper<Scene>, EvalError> {
sce.add_object(obj.clone());
Ok(sce)
}
#[native_lisp_function]
pub fn scene_add_light(
mut sce: ForeignDataWrapper<Scene>,
lgt: ForeignDataWrapper<Light>,
) -> Result<ForeignDataWrapper<Scene>, 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<Point3>,
cnt: ForeignDataWrapper<Point3>,
up: ForeignDataWrapper<Vector3>,
fovy: f64,
w: i64,
h: i64,
) -> Result<ForeignDataWrapper<Camera>, 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<Camera>,
pos: ForeignDataWrapper<Point3>,
cnt: ForeignDataWrapper<Point3>,
up: ForeignDataWrapper<Vector3>,
fovy: f64,
) -> Result<ForeignDataWrapper<Camera>, EvalError> {
Ok(ForeignDataWrapper::new(
cam.to_owned().reposition(*pos, *cnt, *up, fovy),
))
}
#[native_lisp_function(eval)]
pub fn render(
cam: ForeignDataWrapper<Camera>,
sce: ForeignDataWrapper<Scene>,
dpt: i64,
sbp: i64,
out: String,
) -> Result<Expression, EvalError> {
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<Expression, EvalError> {
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 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<Scene, EvalError> {
let scene_fn_call: Expression = [scene_fn.clone(), (t as i64).into()].into();
let scn: ForeignDataWrapper<Scene> = eval(env, scene_fn_call)?.try_into()?;
Ok(scn.to_owned())
};
let ucm = |t: u32, c: &Camera| -> Result<Camera, EvalError> {
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<Camera> = 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<f64, EvalError> {
Ok(x.sin())
}
#[native_lisp_function(eval)]
pub fn cos(x: f64) -> Result<f64, EvalError> {
Ok(x.cos())
}
#[native_lisp_function(eval)]
pub fn add_i(x: i64, y: i64) -> Result<i64, EvalError> {
Ok(x + y)
}
#[native_lisp_function(eval)]
pub fn add_f(x: f64, y: f64) -> Result<f64, EvalError> {
Ok(x + y)
}
#[native_lisp_function]
pub fn vadd_vv(
a: ForeignDataWrapper<Vector3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*a + *b))
}
#[native_lisp_function]
pub fn vadd_vp(
a: ForeignDataWrapper<Vector3>,
b: ForeignDataWrapper<Point3>,
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(*b + *a))
}
#[native_lisp_function]
pub fn vadd_pv(
a: ForeignDataWrapper<Point3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Point3>, 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<i64, EvalError> {
Ok(x - y)
}
#[native_lisp_function(eval)]
pub fn sub_f(x: f64, y: f64) -> Result<f64, EvalError> {
Ok(x - y)
}
#[native_lisp_function]
pub fn sub_vv(
a: ForeignDataWrapper<Vector3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*a - *b))
}
#[native_lisp_function]
pub fn sub_vp(
a: ForeignDataWrapper<Vector3>,
b: ForeignDataWrapper<Point3>,
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(*b - *a))
}
#[native_lisp_function]
pub fn sub_pv(
a: ForeignDataWrapper<Point3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(*a - *b))
}
#[native_lisp_function]
pub fn sub_pp(
a: ForeignDataWrapper<Point3>,
b: ForeignDataWrapper<Point3>,
) -> Result<ForeignDataWrapper<Vector3>, 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<i64, EvalError> {
Ok(x * y)
}
#[native_lisp_function(eval)]
pub fn mul_f(x: f64, y: f64) -> Result<f64, EvalError> {
Ok(x * y)
}
#[native_lisp_function]
pub fn mul_vs(
a: ForeignDataWrapper<Vector3>,
b: f64,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*a * b))
}
#[native_lisp_function]
pub fn mul_sv(
a: f64,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*b * a))
}
#[native_lisp_function]
pub fn mul_ps(
a: ForeignDataWrapper<Point3>,
b: f64,
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(*a * b))
}
#[native_lisp_function]
pub fn mul_sp(
a: f64,
b: ForeignDataWrapper<Point3>,
) -> Result<ForeignDataWrapper<Point3>, 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<f64, EvalError> {
Ok(x as f64 / y as f64)
}
#[native_lisp_function(eval)]
pub fn div_f(x: f64, y: f64) -> Result<f64, EvalError> {
Ok(x / y)
}
#[native_lisp_function]
pub fn div_vs(
a: ForeignDataWrapper<Vector3>,
b: f64,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*a / b))
}
#[native_lisp_function]
pub fn div_sv(
a: f64,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*b / a))
}
#[native_lisp_function]
pub fn div_ps(
a: ForeignDataWrapper<Point3>,
b: f64,
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(*a / b))
}
#[native_lisp_function]
pub fn div_sp(
a: f64,
b: ForeignDataWrapper<Point3>,
) -> Result<ForeignDataWrapper<Point3>, 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<Vector3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<f64, EvalError> {
Ok(a.dot(&b))
}
#[native_lisp_function]
pub fn abs_i(a: i64) -> Result<i64, EvalError> {
Ok(a.abs())
}
#[native_lisp_function]
pub fn abs_f(a: f64) -> Result<f64, EvalError> {
Ok(a.abs())
}
#[native_lisp_function]
pub fn abs_v(a: ForeignDataWrapper<Vector3>) -> Result<f64, EvalError> {
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));
}