2025-01-11 16:33:05 +01:00

283 lines
8.0 KiB
Rust

use crate::raytracer::{scene::Scene, types::Light};
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},
sphere::Sphere,
types::{Color, Material, Point3, RTObjectWrapper, Vector3},
};
#[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 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 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(),
)
}
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 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())),
}
}
#[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]
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 = vadd,
eval,
dispatch = vadd_vv,
dispatch = vadd_vp,
dispatch = vadd_pv
);
#[native_lisp_function]
pub fn vsub_vv(
a: ForeignDataWrapper<Vector3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*a - *b))
}
#[native_lisp_function]
pub fn vsub_vp(
a: ForeignDataWrapper<Vector3>,
b: ForeignDataWrapper<Point3>,
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(*b - *a))
}
#[native_lisp_function]
pub fn vsub_pv(
a: ForeignDataWrapper<Point3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Point3>, EvalError> {
Ok(ForeignDataWrapper::new(*a - *b))
}
native_lisp_function_proxy!(
fname = vsub,
eval,
dispatch = vsub_vv,
dispatch = vsub_vp,
dispatch = vsub_pv
);
#[native_lisp_function]
pub fn vmul_vs(
a: ForeignDataWrapper<Vector3>,
b: f64,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*a * b))
}
#[native_lisp_function]
pub fn vmul_sv(
a: f64,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*b * a))
}
native_lisp_function_proxy!(fname = vmul, eval, dispatch = vmul_vs, dispatch = vmul_sv);
/// 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("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("sphere".to_string(), Expression::Function(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("render".to_string(), Expression::Function(render));
layer.set("sin".to_string(), Expression::Function(sin));
layer.set("cos".to_string(), Expression::Function(cos));
layer.set("vadd".to_string(), Expression::Function(vadd));
layer.set("vsub".to_string(), Expression::Function(vsub));
layer.set("vmul".to_string(), Expression::Function(vmul));
}