Skip to main content

loam_render/
view.rs

1//! Root image space: the eye at the origin looking down -Z. Depth is
2//! `near / -z`, reversed-Z in `Depth32Float`, cleared to zero and compared
3//! `GreaterEqual`; every pass here writes that quantity, and `StandardZ` nodes serve the old paths only.
4
5use glam::{Mat4, Vec3};
6use loam_math::hyperbolic::{hyperboloid_to_klein, poincare_to_hyperboloid, poincare_to_klein};
7pub use loam_math::hyperbolic::{H3_DEPTH_ENVELOPE, H3_DEPTH_SEPARATION, H3_EYE_CHART_REACH};
8use loam_math::{Iso3, Iso3H};
9use loam_runtime::{Eye, Rigid};
10use wgpu::{CompareFunction, TextureFormat};
11
12pub const DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
13pub const DEPTH_COMPARE: CompareFunction = CompareFunction::GreaterEqual;
14pub const DEPTH_CLEAR: f32 = 0.0;
15
16pub fn root_projection(fov_y: f32, aspect: f32, near: f32) -> Mat4 {
17    Mat4::perspective_infinite_reverse_rh(fov_y, aspect, near)
18}
19
20pub fn projective_depth(image: Vec3, near: f32) -> f32 {
21    near / -image.z
22}
23
24pub fn root_view_projection(eye: &Eye) -> Mat4 {
25    let basis = Mat4::from_cols(
26        Vec3::from(eye.right).extend(0.0),
27        Vec3::from(eye.up).extend(0.0),
28        (-Vec3::from(eye.forward)).extend(0.0),
29        Vec3::from(eye.position).extend(1.0),
30    );
31    root_projection(eye.fov_y, eye.aspect, eye.near) * basis.inverse()
32}
33
34pub fn eye_relative(eye: Iso3) -> Mat4 {
35    Mat4::from_rotation_translation(eye.rotation, eye.translation).inverse()
36}
37
38pub fn placement_matrix(placement: Rigid) -> Mat4 {
39    Mat4::from_scale_rotation_translation(
40        Vec3::splat(placement.scale),
41        placement.pose.rotation,
42        placement.pose.translation,
43    )
44}
45
46pub fn placed_view_projection(eye: &Eye, placement: Rigid) -> Mat4 {
47    root_view_projection(eye) * placement_matrix(placement)
48}
49
50pub fn h3_image_of(eye_inverse: &Iso3H, p: Vec3) -> Vec3 {
51    hyperboloid_to_klein(eye_inverse.matrix * poincare_to_hyperboloid(p))
52}
53
54pub fn klein_tessellate_segment(p0: Vec3, p1: Vec3, samples: usize, mut emit: impl FnMut(Vec3)) {
55    let samples = samples.max(1);
56    for i in 0..=samples {
57        emit(poincare_to_klein(p0.lerp(p1, i as f32 / samples as f32)));
58    }
59}
60
61// Chord remainder (1/n)²/8 · max|k''|, with |k''| ≤ 20 |p1 - p0|² max(|p0|, |p1|) for k = 2p / (1 + |p|²).
62pub fn klein_tessellation_error(p0: Vec3, p1: Vec3, samples: usize) -> f32 {
63    let reach = p0.length().max(p1.length());
64    2.5 * (p1 - p0).length_squared() * reach / (samples.max(1) as f32).powi(2)
65}