#[macro_use]
mod support;
macro_rules! impl_camera_tests {
($t:ident, $affine3:ident, $mat4:ident, $mat3:ident, $vec4:ident, $vec3:ident, $quat:ident, $camera:ident) => {
struct AxisConfig {
forward: $vec3,
right: $vec3,
up: $vec3,
}
struct NdcConfig {
z_near: $t,
z_far: $t,
y_down: bool,
}
const EYE: $vec3 = $vec3::ZERO;
const NDC_OPENGL: NdcConfig = NdcConfig {
z_near: -1.0,
z_far: 1.0,
y_down: false,
};
const NDC_VULKAN: NdcConfig = NdcConfig {
z_near: 0.0,
z_far: 1.0,
y_down: true,
};
const NDC_DIRECTX: NdcConfig = NdcConfig {
z_near: 0.0,
z_far: 1.0,
y_down: false,
};
const RH_YUP_AXES: AxisConfig = AxisConfig {
forward: $vec3::NEG_Z,
right: $vec3::X,
up: $vec3::Y,
};
const LH_YUP_AXES: AxisConfig = AxisConfig {
forward: $vec3::Z,
right: $vec3::X,
up: $vec3::Y,
};
#[allow(unused)]
const RH_ZUP_AXES: AxisConfig = AxisConfig {
forward: $vec3::Y,
right: $vec3::X,
up: $vec3::Z,
};
#[allow(unused)]
const LH_ZUP_AXES: AxisConfig = AxisConfig {
forward: $vec3::X,
right: $vec3::Y,
up: $vec3::Z,
};
fn check_view(world: &AxisConfig, view: &$mat4) {
let p = (view * (world.right * 5.0).to_homogeneous()).project();
assert_approx_eq!(p.x, 5.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let p = (view * (world.up * 5.0).to_homogeneous()).project();
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 5.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let fwd = (view * (world.forward * 5.0).to_homogeneous()).project();
let expected_z = handedness_sign(world) * 5.0;
assert_approx_eq!(fwd.x, 0.0, 1e-6);
assert_approx_eq!(fwd.y, 0.0, 1e-6);
assert_approx_eq!(fwd.z, expected_z, 1e-6);
let behind = (view * (-world.forward * 5.0).to_homogeneous()).project();
assert_approx_eq!(behind.x, 0.0, 1e-6);
assert_approx_eq!(behind.y, 0.0, 1e-6);
assert_approx_eq!(behind.z, -expected_z, 1e-6);
let p = (view * EYE.to_homogeneous()).project();
assert_approx_eq!(p, $vec3::ZERO, 1e-6);
}
fn check_proj_direction(world: &AxisConfig, ndc: &NdcConfig, view: &$mat4, proj: &$mat4) {
let pt = (proj * (view * (world.forward * 5.0).to_homogeneous())).project();
assert_approx_eq!(pt.x, 0.0, 1e-6);
assert_approx_eq!(pt.y, 0.0, 1e-6);
let pt =
(proj * (view * (world.forward * 5.0 + world.right).to_homogeneous())).project();
assert!(pt.x > 0.0);
let pt =
(proj * (view * (world.forward * 5.0 - world.right).to_homogeneous())).project();
assert!(pt.x < 0.0);
let pt = (proj * (view * (world.forward * 5.0 + world.up).to_homogeneous())).project();
if ndc.y_down {
assert!(pt.y < 0.0);
} else {
assert!(pt.y > 0.0);
}
let pt = (proj * (view * (world.forward * 5.0 - world.up).to_homogeneous())).project();
if ndc.y_down {
assert!(pt.y > 0.0);
} else {
assert!(pt.y < 0.0);
}
}
fn check_proj_near_far(world: &AxisConfig, ndc: &NdcConfig, view: &$mat4, proj: &$mat4) {
let pt = (proj * (view * (world.forward * 1.0).to_homogeneous())).project();
assert_approx_eq!(pt.x, 0.0, 1e-6);
assert_approx_eq!(pt.y, 0.0, 1e-6);
assert_approx_eq!(pt.z, ndc.z_near, 1e-6);
let pt = (proj * (view * (world.forward * 10.0).to_homogeneous())).project();
assert_approx_eq!(pt.x, 0.0, 1e-6);
assert_approx_eq!(pt.y, 0.0, 1e-6);
assert_approx_eq!(pt.z, ndc.z_far, 1e-6);
}
fn check_proj_near(world: &AxisConfig, ndc: &NdcConfig, view: &$mat4, proj: &$mat4) {
let pt = (proj * (view * (world.forward * 1.0).to_homogeneous())).project();
assert_approx_eq!(pt.x, 0.0, 1e-6);
assert_approx_eq!(pt.y, 0.0, 1e-6);
assert_approx_eq!(pt.z, ndc.z_near, 1e-6);
}
fn check_proj_reverse_near(
world: &AxisConfig,
ndc: &NdcConfig,
view: &$mat4,
proj: &$mat4,
) {
let pt = (proj * (view * (world.forward * 1.0).to_homogeneous())).project();
assert_approx_eq!(pt.x, 0.0, 1e-6);
assert_approx_eq!(pt.y, 0.0, 1e-6);
assert_approx_eq!(pt.z, ndc.z_far, 1e-6);
}
fn check_view_rotation_mat3(world: &AxisConfig, rot: &$mat3) {
let expected_z = handedness_sign(world) * 5.0;
let p = *rot * (world.right * 5.0);
assert_approx_eq!(p.x, 5.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let p = *rot * (world.up * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 5.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let p = *rot * (world.forward * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, expected_z, 1e-6);
let p = *rot * (-world.forward * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, -expected_z, 1e-6);
}
fn handedness_sign(axes: &AxisConfig) -> $t {
axes.right.dot(axes.up.cross(axes.forward)).signum()
}
mod view_rh {
use super::*;
use glam::$camera::rh::view;
mod yup {
use super::*;
impl_view_tests!(RH_YUP_AXES, $mat4);
}
mod zup {
use super::*;
impl_view_tests!(RH_ZUP_AXES, $mat4);
}
}
mod view_lh {
use super::*;
use glam::$camera::lh::view;
mod yup {
use super::*;
impl_view_tests!(LH_YUP_AXES, $mat4);
}
mod zup {
use super::*;
impl_view_tests!(LH_ZUP_AXES, $mat4);
}
}
mod proj_rh {
use super::*;
use glam::$camera::rh::proj;
glam_test!(test_transform_opengl_perspective, {
let fov = $t::to_radians(90.0);
let p = proj::opengl::perspective(fov, 2.0, 5.0, 15.0);
let original = $vec3::new(5.0, 5.0, -15.0);
let projected = p * original.extend(1.0);
assert_approx_eq!(projected, $vec4::new(2.5, 5.0, 15.0, 15.0), 1.0e-6);
});
glam_test!(test_transform_vulkan_perspective, {
let fov = $t::to_radians(90.0);
let p = proj::vulkan::perspective(fov, 2.0, 5.0, 15.0);
let original = $vec3::new(5.0, 5.0, -15.0);
let projected = p * original.extend(1.0);
assert_approx_eq!(projected, $vec4::new(2.5, -5.0, 15.0, 15.0), 1.0e-6);
});
glam_test!(test_transform_directx_perspective, {
let fov = $t::to_radians(90.0);
let p = proj::directx::perspective(fov, 2.0, 5.0, 15.0);
let original = $vec3::new(5.0, 5.0, -15.0);
let projected = p * original.extend(1.0);
assert_approx_eq!(projected, $vec4::new(2.5, 5.0, 15.0, 15.0), 1.0e-6);
});
}
mod proj_lh {
use super::*;
use glam::$camera::lh::proj;
glam_test!(test_perspective_opengl, {
let fov = $t::to_radians(90.0);
let aspect = 2.0;
let near = 5.0;
let far = 15.0;
let p = proj::opengl::perspective(fov, aspect, near, far);
assert_approx_eq!(p.x_axis, $vec4::new(0.5, 0.0, 0.0, 0.0));
assert_approx_eq!(p.y_axis, $vec4::new(0.0, 1.0, 0.0, 0.0));
assert_approx_eq!(p.z_axis, $vec4::new(0.0, 0.0, 2.0, 1.0));
assert_approx_eq!(p.w_axis, $vec4::new(0.0, 0.0, -15.0, 0.0));
});
glam_test!(test_orthographic_opengl, {
let p = proj::opengl::orthographic(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
assert_approx_eq!(p.z_axis, $vec4::new(0.0, 0.0, 0.1, 0.0));
assert_approx_eq!(p.w_axis, $vec4::new(0.0, 0.0, 0.0, 1.0));
});
glam_test!(test_frustum_opengl, {
let fov = $t::to_radians(90.0);
let aspect = 2.0;
let near = 5.0;
let far = 15.0;
let f = (0.5 * fov).tan();
let height = near * f;
let width = height * aspect;
let p = proj::opengl::frustum(-width, width, -height, height, near, far);
assert_approx_eq!(p.x_axis, $vec4::new(0.5, 0.0, 0.0, 0.0));
assert_approx_eq!(p.y_axis, $vec4::new(0.0, 1.0, 0.0, 0.0));
assert_approx_eq!(p.z_axis, $vec4::new(0.0, 0.0, 2.0, 1.0));
assert_approx_eq!(p.w_axis, $vec4::new(0.0, 0.0, -15.0, 0.0));
});
glam_test!(test_transform_opengl_perspective, {
let fov = $t::to_radians(90.0);
let p = proj::opengl::perspective(fov, 2.0, 5.0, 15.0);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = p * original.extend(1.0);
assert_approx_eq!(projected, $vec4::new(2.5, 5.0, 15.0, 15.0), 1.0e-6);
});
glam_test!(test_transform_vulkan_perspective, {
let fov = $t::to_radians(90.0);
let p = proj::vulkan::perspective(fov, 2.0, 5.0, 15.0);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = p * original.extend(1.0);
assert_approx_eq!(projected, $vec4::new(2.5, -5.0, 15.0, 15.0), 1.0e-6);
});
glam_test!(test_transform_directx_perspective, {
let fov = $t::to_radians(90.0);
let p = proj::directx::perspective(fov, 2.0, 5.0, 15.0);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = p * original.extend(1.0);
assert_approx_eq!(projected, $vec4::new(2.5, 5.0, 15.0, 15.0), 1.0e-6);
});
}
mod pipeline_rh {
use super::*;
use glam::$camera::rh::{proj, view};
mod yup {
use super::*;
impl_pipeline_tests!($t, RH_YUP_AXES);
}
mod zup {
use super::*;
impl_pipeline_tests!($t, RH_ZUP_AXES);
}
mod gltf {
use super::*;
const GLTF_AXES: AxisConfig = AxisConfig {
forward: $vec3::Z,
right: $vec3::NEG_X,
up: $vec3::Y,
};
impl_pipeline_tests!($t, GLTF_AXES);
}
}
mod pipeline_lh {
use super::*;
use glam::$camera::lh::{proj, view};
mod yup {
use super::*;
impl_pipeline_tests!($t, LH_YUP_AXES);
}
mod zup {
use super::*;
impl_pipeline_tests!($t, LH_ZUP_AXES);
}
}
mod deprecated {
#![allow(deprecated)]
use super::*;
glam_test!(test_quat_look_at, {
let eye = $vec3::new(0.0, 0.0, -5.0);
let center = $vec3::new(0.0, 0.0, 0.0);
let up = $vec3::new(1.0, 0.0, 0.0);
let point = $vec3::new(1.0, 0.0, 0.0);
let deprecated_lh = $quat::look_at_lh(eye, center, up);
let deprecated_rh = $quat::look_at_rh(eye, center, up);
let lh = $camera::lh::view::look_at_quat(eye, center, up);
let rh = $camera::rh::view::look_at_quat(eye, center, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh * point, $vec3::new(0.0, 1.0, 0.0));
assert_approx_eq!(rh * point, $vec3::new(0.0, 1.0, 0.0));
let dir = (center - eye).normalize();
let deprecated_lh = $quat::look_to_lh(dir, up);
let deprecated_rh = $quat::look_to_rh(dir, up);
let lh = $camera::lh::view::look_to_quat(dir, up);
let rh = $camera::rh::view::look_to_quat(dir, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh * point, $vec3::new(0.0, 1.0, 0.0));
assert_approx_eq!(rh * point, $vec3::new(0.0, 1.0, 0.0));
should_glam_assert!({ $camera::lh::view::look_to_quat($vec3::ONE, $vec3::ZERO) });
should_glam_assert!({ $camera::lh::view::look_to_quat($vec3::ZERO, $vec3::ONE) });
should_glam_assert!({ $camera::rh::view::look_to_quat($vec3::ONE, $vec3::ZERO) });
should_glam_assert!({ $camera::rh::view::look_to_quat($vec3::ZERO, $vec3::ONE) });
});
glam_test!(test_mat3_look_at, {
let eye = $vec3::new(0.0, 0.0, -5.0);
let center = $vec3::new(0.0, 0.0, 0.0);
let up = $vec3::new(1.0, 0.0, 0.0);
let point = $vec3::new(1.0, 0.0, 0.0);
let deprecated_lh = $mat3::look_at_lh(eye, center, up);
let deprecated_rh = $mat3::look_at_rh(eye, center, up);
let lh = $camera::lh::view::look_at_mat3(eye, center, up);
let rh = $camera::rh::view::look_at_mat3(eye, center, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh * point, $vec3::new(0.0, 1.0, 0.0));
assert_approx_eq!(rh * point, $vec3::new(0.0, 1.0, 0.0));
let dir = (center - eye).normalize();
let deprecated_lh = $mat3::look_to_lh(dir, up);
let deprecated_rh = $mat3::look_to_rh(dir, up);
let lh = $camera::lh::view::look_to_mat3(dir, up);
let rh = $camera::rh::view::look_to_mat3(dir, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh * point, $vec3::new(0.0, 1.0, 0.0));
assert_approx_eq!(rh * point, $vec3::new(0.0, 1.0, 0.0));
should_glam_assert!({ $camera::lh::view::look_to_mat3($vec3::ONE, $vec3::ZERO) });
should_glam_assert!({ $camera::lh::view::look_to_mat3($vec3::ZERO, $vec3::ONE) });
should_glam_assert!({ $camera::rh::view::look_to_mat3($vec3::ONE, $vec3::ZERO) });
should_glam_assert!({ $camera::rh::view::look_to_mat3($vec3::ZERO, $vec3::ONE) });
});
glam_test!(test_affine3_look_at, {
let eye = $vec3::new(0.0, 0.0, -5.0);
let center = $vec3::new(0.0, 0.0, 0.0);
let up = $vec3::new(1.0, 0.0, 0.0);
let point = $vec3::new(1.0, 0.0, 0.0);
let deprecated_lh = $affine3::look_at_lh(eye, center, up);
let deprecated_rh = $affine3::look_at_rh(eye, center, up);
let lh = $camera::lh::view::look_at_affine3(eye, center, up);
let rh = $camera::rh::view::look_at_affine3(eye, center, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh.transform_point3(point), $vec3::new(0.0, 1.0, 5.0));
assert_approx_eq!(rh.transform_point3(point), $vec3::new(0.0, 1.0, -5.0));
let dir = (center - eye).normalize();
let deprecated_lh = $affine3::look_to_lh(eye, dir, up);
let deprecated_rh = $affine3::look_to_rh(eye, dir, up);
let lh = $camera::lh::view::look_to_affine3(eye, dir, up);
let rh = $camera::rh::view::look_to_affine3(eye, dir, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh.transform_point3(point), $vec3::new(0.0, 1.0, 5.0));
assert_approx_eq!(rh.transform_point3(point), $vec3::new(0.0, 1.0, -5.0));
should_glam_assert!({
$camera::lh::view::look_at_affine3($vec3::ONE, $vec3::ZERO, $vec3::ZERO)
});
should_glam_assert!({
$camera::rh::view::look_at_affine3($vec3::ONE, $vec3::ZERO, $vec3::ZERO)
});
});
glam_test!(test_mat4_look_at, {
let eye = $vec3::new(0.0, 0.0, -5.0);
let center = $vec3::new(0.0, 0.0, 0.0);
let up = $vec3::new(1.0, 0.0, 0.0);
let point = $vec3::new(1.0, 0.0, 0.0);
let deprecated_lh = $mat4::look_at_lh(eye, center, up);
let deprecated_rh = $mat4::look_at_rh(eye, center, up);
let lh = $camera::lh::view::look_at_mat4(eye, center, up);
let rh = $camera::rh::view::look_at_mat4(eye, center, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh.transform_point3(point), $vec3::new(0.0, 1.0, 5.0));
assert_approx_eq!(rh.transform_point3(point), $vec3::new(0.0, 1.0, -5.0));
let dir = (center - eye).normalize();
let deprecated_lh = $mat4::look_to_lh(eye, dir, up);
let deprecated_rh = $mat4::look_to_rh(eye, dir, up);
let lh = $camera::lh::view::look_to_mat4(eye, dir, up);
let rh = $camera::rh::view::look_to_mat4(eye, dir, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh.transform_point3(point), $vec3::new(0.0, 1.0, 5.0));
assert_approx_eq!(rh.transform_point3(point), $vec3::new(0.0, 1.0, -5.0));
should_glam_assert!({
$camera::lh::view::look_to_mat4($vec3::ZERO, $vec3::ONE, $vec3::ZERO)
});
should_glam_assert!({
$camera::lh::view::look_to_mat4($vec3::ZERO, $vec3::ZERO, $vec3::ONE)
});
should_glam_assert!({
$camera::rh::view::look_to_mat4($vec3::ZERO, $vec3::ONE, $vec3::ZERO)
});
should_glam_assert!({
$camera::rh::view::look_to_mat4($vec3::ZERO, $vec3::ZERO, $vec3::ONE)
});
});
glam_test!(test_mat4_frustum_gl_rh, {
let fov_y_radians = $t::to_radians(90.0);
let aspect_ratio = 2.0;
let z_near = 5.0;
let z_far = 15.0;
let f = (0.5 * fov_y_radians).tan();
let height = z_near * f;
let width = height * aspect_ratio;
let deprecated_frustum =
$mat4::frustum_rh_gl(-width, width, -height, height, z_near, z_far);
let deprecated_perspective =
$mat4::perspective_rh_gl(fov_y_radians, aspect_ratio, z_near, z_far);
let frustum = $camera::rh::proj::opengl::frustum(
-width, width, -height, height, z_near, z_far,
);
let perspective = $camera::rh::proj::opengl::perspective(
fov_y_radians,
aspect_ratio,
z_near,
z_far,
);
assert_approx_eq!(deprecated_frustum, frustum);
assert_approx_eq!(deprecated_perspective, perspective);
assert_approx_eq!(frustum, perspective);
should_glam_assert!({
$camera::rh::proj::opengl::frustum(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0)
});
should_glam_assert!({
$camera::rh::proj::opengl::frustum(-1.0, 1.0, -1.0, 1.0, 1.0, 0.0)
});
});
glam_test!(test_mat4_frustum_lh, {
let fov_y_radians = $t::to_radians(90.0);
let aspect_ratio = 2.0;
let z_near = 5.0;
let z_far = 15.0;
let f = (0.5 * fov_y_radians).tan();
let height = z_near * f;
let width = height * aspect_ratio;
let deprecated_frustum =
$mat4::frustum_lh(-width, width, -height, height, z_near, z_far);
let deprecated_perspective =
$mat4::perspective_lh(fov_y_radians, aspect_ratio, z_near, z_far);
let frustum = $camera::lh::proj::directx::frustum(
-width, width, -height, height, z_near, z_far,
);
let perspective = $camera::lh::proj::directx::perspective(
fov_y_radians,
aspect_ratio,
z_near,
z_far,
);
assert_approx_eq!(deprecated_frustum, frustum);
assert_approx_eq!(deprecated_perspective, perspective);
assert_approx_eq!(frustum, perspective);
should_glam_assert!({
$camera::lh::proj::directx::frustum(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0)
});
should_glam_assert!({
$camera::lh::proj::directx::frustum(-1.0, 1.0, -1.0, 1.0, 1.0, 0.0)
});
});
glam_test!(test_mat4_frustum_rh, {
let fov_y_radians = $t::to_radians(90.0);
let aspect_ratio = 2.0;
let z_near = 5.0;
let z_far = 15.0;
let f = (0.5 * fov_y_radians).tan();
let height = z_near * f;
let width = height * aspect_ratio;
let deprecated_frustum =
$mat4::frustum_rh(-width, width, -height, height, z_near, z_far);
let deprecated_perspective =
$mat4::perspective_rh(fov_y_radians, aspect_ratio, z_near, z_far);
let frustum = $camera::rh::proj::directx::frustum(
-width, width, -height, height, z_near, z_far,
);
let perspective = $camera::rh::proj::directx::perspective(
fov_y_radians,
aspect_ratio,
z_near,
z_far,
);
assert_approx_eq!(deprecated_frustum, frustum);
assert_approx_eq!(deprecated_perspective, perspective);
assert_approx_eq!(frustum, perspective);
should_glam_assert!({
$camera::rh::proj::directx::frustum(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0)
});
should_glam_assert!({
$camera::rh::proj::directx::frustum(-1.0, 1.0, -1.0, 1.0, 1.0, 0.0)
});
});
glam_test!(test_mat4_perspective_gl_rh, {
let deprecated_projection =
$mat4::perspective_rh_gl($t::to_radians(90.0), 2.0, 5.0, 15.0);
let projection =
$camera::rh::proj::opengl::perspective($t::to_radians(90.0), 2.0, 5.0, 15.0);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec3::new(5.0, 5.0, -15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 15.0, 15.0), projected);
let original = $vec3::new(5.0, 5.0, -5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, -5.0, 5.0), projected);
});
glam_test!(test_mat4_perspective_lh, {
let deprecated_projection =
$mat4::perspective_lh($t::to_radians(90.0), 2.0, 5.0, 15.0);
let projection =
$camera::lh::proj::directx::perspective($t::to_radians(90.0), 2.0, 5.0, 15.0);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 15.0, 15.0), projected, 1e-6);
let original = $vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 0.0, 5.0), projected, 1e-6);
should_glam_assert!({
$camera::lh::proj::directx::perspective(0.0, 1.0, 1.0, 0.0)
});
should_glam_assert!({
$camera::lh::proj::directx::perspective(0.0, 1.0, 0.0, 1.0)
});
});
glam_test!(test_mat4_perspective_infinite_lh, {
let deprecated_projection =
$mat4::perspective_infinite_lh($t::to_radians(90.0), 2.0, 5.0);
let projection = $camera::lh::proj::directx::perspective_infinite(
$t::to_radians(90.0),
2.0,
5.0,
);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 10.0, 15.0), projected, 1e-6);
let original = $vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 0.0, 5.0), projected, 1e-6);
should_glam_assert!({
$camera::lh::proj::directx::perspective_infinite(0.0, 1.0, 0.0)
});
});
glam_test!(test_mat4_perspective_infinite_reverse_lh, {
let deprecated_projection =
$mat4::perspective_infinite_reverse_lh($t::to_radians(90.0), 2.0, 5.0);
let projection = $camera::lh::proj::directx::perspective_infinite_reverse(
$t::to_radians(90.0),
2.0,
5.0,
);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 5.0, 15.0), projected, 1e-6);
let original = $vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 5.0, 5.0), projected, 1e-6);
should_glam_assert!({
$camera::lh::proj::directx::perspective_infinite_reverse(0.0, 1.0, 0.0)
});
});
glam_test!(test_mat4_perspective_rh, {
let deprecated_projection =
$mat4::perspective_rh($t::to_radians(90.0), 2.0, 5.0, 15.0);
let projection =
$camera::rh::proj::directx::perspective($t::to_radians(90.0), 2.0, 5.0, 15.0);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, -30.0, -15.0), projected, 1e-6);
let original = $vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, -15.0, -5.0), projected, 1e-6);
should_glam_assert!({
$camera::lh::proj::directx::perspective(0.0, 1.0, 1.0, 0.0)
});
should_glam_assert!({
$camera::lh::proj::directx::perspective(0.0, 1.0, 0.0, 1.0)
});
});
glam_test!(test_mat4_perspective_infinite_rh, {
let deprecated_projection =
$mat4::perspective_infinite_rh($t::to_radians(90.0), 2.0, 5.0);
let projection = $camera::rh::proj::directx::perspective_infinite(
$t::to_radians(90.0),
2.0,
5.0,
);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, -20.0, -15.0), projected);
let original = $vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, -10.0, -5.0), projected);
should_glam_assert!({
$camera::rh::proj::directx::perspective_infinite(0.0, 1.0, 0.0)
});
});
glam_test!(test_mat4_perspective_infinite_reverse_rh, {
let deprecated_projection =
$mat4::perspective_infinite_reverse_rh($t::to_radians(90.0), 2.0, 5.0);
let projection = $camera::rh::proj::directx::perspective_infinite_reverse(
$t::to_radians(90.0),
2.0,
5.0,
);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 5.0, -15.0), projected);
let original = $vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!($vec4::new(2.5, 5.0, 5.0, -5.0), projected);
should_glam_assert!({
$camera::rh::proj::directx::perspective_infinite_reverse(0.0, 1.0, 0.0)
});
});
glam_test!(test_mat4_orthographic_gl_rh, {
let deprecated_projection =
$mat4::orthographic_rh_gl(-10.0, 10.0, -5.0, 5.0, 1.0, 11.0);
let projection =
$camera::rh::proj::opengl::orthographic(-10.0, 10.0, -5.0, 5.0, 1.0, 11.0);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec4::new(5.0, 5.0, -5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, $vec4::new(0.5, 1.0, -0.2, 1.0));
});
glam_test!(test_mat4_orthographic_rh, {
let deprecated_projection =
$mat4::orthographic_rh(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
let projection =
$camera::rh::proj::directx::orthographic(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec4::new(5.0, 5.0, -5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, $vec4::new(0.5, 1.0, 0.75, 1.0));
let original = $vec4::new(5.0, 5.0, 5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, $vec4::new(0.5, 1.0, 0.25, 1.0));
});
glam_test!(test_mat4_orthographic_lh, {
let deprecated_projection =
$mat4::orthographic_lh(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
let projection =
$camera::lh::proj::directx::orthographic(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
assert_approx_eq!(deprecated_projection, projection);
let original = $vec4::new(5.0, 5.0, -5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, $vec4::new(0.5, 1.0, 0.25, 1.0));
let original = $vec4::new(5.0, 5.0, 5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, $vec4::new(0.5, 1.0, 0.75, 1.0));
});
}
};
}
macro_rules! impl_view_tests {
($world:ident, $mat4:ident) => {
glam_test!(test_look_at_mat4, {
let m = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
check_view(&$world, &m);
});
glam_test!(test_look_to_mat4, {
let m = view::look_to_mat4(EYE, $world.forward, $world.up);
check_view(&$world, &m);
});
glam_test!(test_look_at_affine3, {
let a = view::look_at_affine3(EYE, $world.forward * 5.0, $world.up);
let m = $mat4::from(a);
check_view(&$world, &m);
});
glam_test!(test_look_to_affine3, {
let a = view::look_to_affine3(EYE, $world.forward, $world.up);
let m = $mat4::from(a);
check_view(&$world, &m);
});
glam_test!(test_look_at_mat3, {
let rot = view::look_at_mat3(EYE, $world.forward * 5.0, $world.up);
check_view_rotation_mat3(&$world, &rot);
});
glam_test!(test_look_to_mat3, {
let rot = view::look_to_mat3($world.forward, $world.up);
check_view_rotation_mat3(&$world, &rot);
});
glam_test!(test_look_at_quat, {
let q = view::look_at_quat(EYE, $world.forward * 5.0, $world.up);
let expected_z = handedness_sign(&$world) * 5.0;
let p = q * ($world.right * 5.0);
assert_approx_eq!(p.x, 5.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let p = q * ($world.up * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 5.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let p = q * ($world.forward * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, expected_z, 1e-6);
let p = q * (-$world.forward * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, -expected_z, 1e-6);
});
glam_test!(test_look_to_quat, {
let q = view::look_to_quat($world.forward, $world.up);
let expected_z = handedness_sign(&$world) * 5.0;
let p = q * ($world.right * 5.0);
assert_approx_eq!(p.x, 5.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let p = q * ($world.up * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 5.0, 1e-6);
assert_approx_eq!(p.z, 0.0, 1e-6);
let p = q * ($world.forward * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, expected_z, 1e-6);
let p = q * (-$world.forward * 5.0);
assert_approx_eq!(p.x, 0.0, 1e-6);
assert_approx_eq!(p.y, 0.0, 1e-6);
assert_approx_eq!(p.z, -expected_z, 1e-6);
});
};
}
macro_rules! impl_pipeline_tests {
($t:ident, $world:ident) => {
glam_test!(test_opengl_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::opengl::perspective($t::to_radians(90.0), 1.0, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_OPENGL, &v, &p);
check_proj_near_far(&$world, &NDC_OPENGL, &v, &p);
});
glam_test!(test_vulkan_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::vulkan::perspective($t::to_radians(90.0), 1.0, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_VULKAN, &v, &p);
check_proj_near_far(&$world, &NDC_VULKAN, &v, &p);
});
glam_test!(test_directx_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::directx::perspective($t::to_radians(90.0), 1.0, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_DIRECTX, &v, &p);
check_proj_near_far(&$world, &NDC_DIRECTX, &v, &p);
});
glam_test!(test_opengl_orthographic_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::opengl::orthographic(-5.0, 5.0, -5.0, 5.0, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_OPENGL, &v, &p);
check_proj_near_far(&$world, &NDC_OPENGL, &v, &p);
});
glam_test!(test_vulkan_orthographic_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::vulkan::orthographic(-5.0, 5.0, -5.0, 5.0, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_VULKAN, &v, &p);
check_proj_near_far(&$world, &NDC_VULKAN, &v, &p);
});
glam_test!(test_directx_orthographic_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::directx::orthographic(-5.0, 5.0, -5.0, 5.0, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_DIRECTX, &v, &p);
check_proj_near_far(&$world, &NDC_DIRECTX, &v, &p);
});
glam_test!(test_opengl_frustum_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let fov = $t::to_radians(90.0);
let f = (0.5 * fov).tan();
let height = 1.0 * f;
let width = height * 1.0;
let p = proj::opengl::frustum(-width, width, -height, height, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_OPENGL, &v, &p);
check_proj_near_far(&$world, &NDC_OPENGL, &v, &p);
});
glam_test!(test_vulkan_frustum_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let fov = $t::to_radians(90.0);
let f = (0.5 * fov).tan();
let height = 1.0 * f;
let width = height * 1.0;
let p = proj::vulkan::frustum(-width, width, -height, height, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_VULKAN, &v, &p);
check_proj_near_far(&$world, &NDC_VULKAN, &v, &p);
});
glam_test!(test_directx_frustum_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let fov = $t::to_radians(90.0);
let f = (0.5 * fov).tan();
let height = 1.0 * f;
let width = height * 1.0;
let p = proj::directx::frustum(-width, width, -height, height, 1.0, 10.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_DIRECTX, &v, &p);
check_proj_near_far(&$world, &NDC_DIRECTX, &v, &p);
});
glam_test!(test_vulkan_infinite_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::vulkan::perspective_infinite($t::to_radians(90.0), 1.0, 1.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_VULKAN, &v, &p);
check_proj_near(&$world, &NDC_VULKAN, &v, &p);
});
glam_test!(test_directx_infinite_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::directx::perspective_infinite($t::to_radians(90.0), 1.0, 1.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_DIRECTX, &v, &p);
check_proj_near(&$world, &NDC_DIRECTX, &v, &p);
});
glam_test!(test_vulkan_infinite_reverse_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::vulkan::perspective_infinite_reverse($t::to_radians(90.0), 1.0, 1.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_VULKAN, &v, &p);
check_proj_reverse_near(&$world, &NDC_VULKAN, &v, &p);
});
glam_test!(test_directx_infinite_reverse_pipeline, {
let v = view::look_at_mat4(EYE, $world.forward * 5.0, $world.up);
let p = proj::directx::perspective_infinite_reverse($t::to_radians(90.0), 1.0, 1.0);
check_view(&$world, &v);
check_proj_direction(&$world, &NDC_DIRECTX, &v, &p);
check_proj_reverse_near(&$world, &NDC_DIRECTX, &v, &p);
});
};
}
mod camera {
use glam::{camera, Affine3, Mat3, Mat4, Quat, Vec3, Vec4};
impl_camera_tests!(f32, Affine3, Mat4, Mat3, Vec4, Vec3, Quat, camera);
mod affine3a {
use glam::camera::lh::view as lh_view;
use glam::camera::rh::view as rh_view;
use glam::Vec3;
glam_test!(test_rh_look_at_affine3a, {
let eye = Vec3::new(0.0, 0.0, -5.0);
let center = Vec3::ZERO;
let up = Vec3::new(1.0, 0.0, 0.0);
let point = Vec3::new(1.0, 0.0, 0.0);
let a = rh_view::look_at_affine3a(eye, center, up);
assert_approx_eq!(a.transform_point3(point), Vec3::new(0.0, 1.0, -5.0));
});
glam_test!(test_rh_look_to_affine3a, {
let eye = Vec3::new(0.0, 0.0, -5.0);
let dir = Vec3::new(0.0, 0.0, 1.0);
let up = Vec3::new(1.0, 0.0, 0.0);
let point = Vec3::new(1.0, 0.0, 0.0);
let a = rh_view::look_to_affine3a(eye, dir, up);
assert_approx_eq!(a.transform_point3(point), Vec3::new(0.0, 1.0, -5.0));
});
glam_test!(test_lh_look_at_affine3a, {
let eye = Vec3::new(0.0, 0.0, -5.0);
let center = Vec3::ZERO;
let up = Vec3::new(1.0, 0.0, 0.0);
let point = Vec3::new(1.0, 0.0, 0.0);
let a = lh_view::look_at_affine3a(eye, center, up);
assert_approx_eq!(a.transform_point3(point), Vec3::new(0.0, 1.0, 5.0));
});
glam_test!(test_lh_look_to_affine3a, {
let eye = Vec3::new(0.0, 0.0, -5.0);
let dir = Vec3::new(0.0, 0.0, 1.0);
let up = Vec3::new(1.0, 0.0, 0.0);
let point = Vec3::new(1.0, 0.0, 0.0);
let a = lh_view::look_to_affine3a(eye, dir, up);
assert_approx_eq!(a.transform_point3(point), Vec3::new(0.0, 1.0, 5.0));
});
}
mod mat3a {
use glam::camera::lh::view as lh_view;
use glam::camera::rh::view as rh_view;
use glam::{Vec3, Vec3A};
glam_test!(test_rh_look_at_mat3a, {
let eye = Vec3::new(0.0, 0.0, -5.0);
let center = Vec3::ZERO;
let up = Vec3::new(1.0, 0.0, 0.0);
let rot = rh_view::look_at_mat3a(eye, center, up);
assert_approx_eq!(rot * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
});
glam_test!(test_rh_look_to_mat3a, {
let dir = Vec3::new(0.0, 0.0, 1.0);
let up = Vec3::new(1.0, 0.0, 0.0);
let rot = rh_view::look_to_mat3a(dir, up);
assert_approx_eq!(rot * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
});
glam_test!(test_lh_look_at_mat3a, {
let eye = Vec3::new(0.0, 0.0, -5.0);
let center = Vec3::ZERO;
let up = Vec3::new(1.0, 0.0, 0.0);
let rot = lh_view::look_at_mat3a(eye, center, up);
assert_approx_eq!(rot * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
});
glam_test!(test_lh_look_to_mat3a, {
let dir = Vec3::new(0.0, 0.0, 1.0);
let up = Vec3::new(1.0, 0.0, 0.0);
let rot = lh_view::look_to_mat3a(dir, up);
assert_approx_eq!(rot * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
});
mod deprecated {
#![allow(deprecated)]
use glam::camera::lh::view as lh_view;
use glam::camera::rh::view as rh_view;
use glam::{Mat3A, Vec3, Vec3A};
glam_test!(test_mat3a_look_at, {
let eye = Vec3::new(0.0, 0.0, -5.0);
let center = Vec3::ZERO;
let up = Vec3::new(1.0, 0.0, 0.0);
let deprecated_lh = Mat3A::look_at_lh(eye, center, up);
let deprecated_rh = Mat3A::look_at_rh(eye, center, up);
let lh = lh_view::look_at_mat3a(eye, center, up);
let rh = rh_view::look_at_mat3a(eye, center, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
assert_approx_eq!(rh * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
let dir = (center - eye).normalize();
let deprecated_lh = Mat3A::look_to_lh(dir, up);
let deprecated_rh = Mat3A::look_to_rh(dir, up);
let lh = lh_view::look_to_mat3a(dir, up);
let rh = rh_view::look_to_mat3a(dir, up);
assert_approx_eq!(deprecated_lh, lh);
assert_approx_eq!(deprecated_rh, rh);
assert_approx_eq!(lh * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
assert_approx_eq!(rh * Vec3A::X, Vec3A::new(0.0, 1.0, 0.0));
should_glam_assert!({ lh_view::look_to_mat3a(Vec3::ONE, Vec3::ZERO) });
should_glam_assert!({ lh_view::look_to_mat3a(Vec3::ZERO, Vec3::ONE) });
should_glam_assert!({ rh_view::look_to_mat3a(Vec3::ONE, Vec3::ZERO) });
should_glam_assert!({ rh_view::look_to_mat3a(Vec3::ZERO, Vec3::ONE) });
});
}
}
}
#[cfg(feature = "f64")]
mod dcamera {
use glam::{dcamera, DAffine3, DMat3, DMat4, DQuat, DVec3, DVec4};
impl_camera_tests!(f64, DAffine3, DMat4, DMat3, DVec4, DVec3, DQuat, dcamera);
}