584 lines
15 KiB
Rust
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));
|
|
}
|