use codecraft::glam::Vec3;
use codecraft::render3d::{Camera, Ray};
const WIDTH: f32 = 800.0;
const HEIGHT: f32 = 600.0;
fn camera() -> Camera {
Camera::looking_at(Vec3::new(0.0, 0.28, 0.34), Vec3::new(0.0, 0.015, 0.0))
}
#[test]
fn the_centre_of_the_screen_looks_at_what_the_camera_looks_at() {
let camera = camera();
let ray = camera.ray((WIDTH * 0.5, HEIGHT * 0.5), WIDTH, HEIGHT);
let hit = ray
.plane_hit(0.015)
.expect("the camera points at the board");
assert!(
hit.x.abs() < 1e-3,
"dead centre horizontally, got {}",
hit.x
);
assert!(hit.z.abs() < 1e-3, "dead centre in depth, got {}", hit.z);
}
#[test]
fn the_ray_starts_at_the_eye_and_points_into_the_scene() {
let camera = camera();
let ray = camera.ray((WIDTH * 0.5, HEIGHT * 0.5), WIDTH, HEIGHT);
assert!(
(ray.origin - camera.eye).length() < 0.05,
"the ray should start at the near plane, right by the eye",
);
assert!(ray.direction.y < 0.0, "the camera looks down at the board");
assert!(
(ray.direction.length() - 1.0).abs() < 1e-5,
"direction is a unit vector"
);
}
#[test]
fn moving_the_cursor_moves_the_hit_the_same_way() {
let camera = camera();
let at = |x: f32, y: f32| {
camera
.ray((x, y), WIDTH, HEIGHT)
.plane_hit(0.0)
.expect("looking at the board")
};
let centre = at(WIDTH * 0.5, HEIGHT * 0.5);
let right = at(WIDTH * 0.75, HEIGHT * 0.5);
let down = at(WIDTH * 0.5, HEIGHT * 0.75);
assert!(right.x > centre.x, "cursor right, hit right");
assert!(down.z > centre.z, "cursor down, hit nearer the viewer");
}
#[test]
fn a_ray_pointing_away_from_the_board_hits_nothing() {
let upwards = Ray {
origin: Vec3::new(0.0, 1.0, 0.0),
direction: Vec3::Y,
};
assert!(upwards.plane_hit(0.0).is_none());
let parallel = Ray {
origin: Vec3::new(0.0, 1.0, 0.0),
direction: Vec3::X,
};
assert!(parallel.plane_hit(0.0).is_none());
let leaving = Ray {
origin: Vec3::ZERO,
direction: Vec3::Y,
};
assert_eq!(leaving.plane_hit(0.0), Some(Vec3::ZERO));
}
#[test]
fn a_plane_hit_lands_on_the_plane_it_was_asked_for() {
let camera = camera();
let ray = camera.ray((WIDTH * 0.4, HEIGHT * 0.6), WIDTH, HEIGHT);
for height in [0.0, 0.02, 0.05] {
let hit = ray.plane_hit(height).unwrap();
assert!(
(hit.y - height).abs() < 1e-5,
"asked for y = {height}, got {}",
hit.y,
);
}
}
fn overhead() -> Camera {
Camera::looking_at(Vec3::new(0.0, 0.5, 0.0), Vec3::ZERO)
.orthographic(-0.18, 0.18, -0.18, 0.18, 0.01, 10.0)
}
#[test]
fn picking_still_works_looking_straight_down_through_a_box() {
let camera = overhead();
let at = |x: f32, y: f32| {
camera
.ray((x, y), WIDTH, HEIGHT)
.plane_hit(0.0)
.expect("the whole board is under this camera")
};
let centre = at(WIDTH * 0.5, HEIGHT * 0.5);
assert!(centre.x.abs() < 1e-5 && centre.z.abs() < 1e-5, "{centre:?}");
let right = at(WIDTH, HEIGHT * 0.5);
assert!((right.x - 0.18).abs() < 1e-4, "{}", right.x);
let up = at(WIDTH * 0.5, 0.0);
assert!((up.z + 0.18).abs() < 1e-4, "{}", up.z);
}
#[test]
fn a_box_shows_the_same_size_wherever_the_cursor_is() {
let camera = overhead();
let hit = |x: f32| {
camera
.ray((x, HEIGHT * 0.5), WIDTH, HEIGHT)
.plane_hit(0.0)
.unwrap()
};
let inner = hit(WIDTH * 0.75).x - hit(WIDTH * 0.5).x;
let outer = hit(WIDTH) - hit(WIDTH * 0.75);
assert!((inner - outer.x).abs() < 1e-4, "{inner} vs {}", outer.x);
}
#[test]
fn every_ray_through_a_box_points_the_same_way() {
let camera = overhead();
let middle = camera.ray((WIDTH * 0.5, HEIGHT * 0.5), WIDTH, HEIGHT);
let corner = camera.ray((0.0, 0.0), WIDTH, HEIGHT);
assert!(
(middle.direction - corner.direction).length() < 1e-5,
"parallel projection means parallel rays: {:?} vs {:?}",
middle.direction,
corner.direction,
);
assert!(
middle.direction.y < -0.99,
"and they all point straight down"
);
assert!((middle.origin - corner.origin).length() > 0.1);
}
#[test]
fn looking_straight_down_gives_a_picture_rather_than_nothing() {
let camera = overhead();
let matrix = camera.view_proj(WIDTH / HEIGHT);
assert!(
matrix.to_cols_array().iter().all(|value| value.is_finite()),
"{matrix:?}",
);
let far = matrix * Vec3::new(0.0, 0.0, -0.1).extend(1.0);
assert!(far.y / far.w > 0.0, "{far:?}");
}
#[test]
fn a_box_draws_things_at_the_same_size_however_far_away_they_are() {
let camera = overhead();
let matrix = camera.view_proj(WIDTH / HEIGHT);
let width = |height: f32| {
let left = matrix * Vec3::new(-0.09, height, 0.0).extend(1.0);
let right = matrix * Vec3::new(0.09, height, 0.0).extend(1.0);
right.x / right.w - left.x / left.w
};
assert!(
(width(0.0) - width(0.05)).abs() < 1e-5,
"{} vs {}",
width(0.0),
width(0.05)
);
}