1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//! Visibility calculation.

use crate::{Attribute, Input};
use arctk::{geom::Ray, phys::Crossing};

/// Calculate the occlusion experienced over a distance along ray.
/// Zero indicates full occlusion.
/// Unity indicates full view.
#[inline]
#[must_use]
pub fn occlusion(input: &Input, mut ray: Ray, mut dist: f64) -> f64 {
    debug_assert!(dist > 0.0);

    let bump_dist = input.sett.bump_dist();
    let loop_limit = input.sett.loop_limit();
    let min_weight = input.sett.min_weight();

    let mut vis = 1.0;
    let mut num_loops = 0;
    while let Some(hit) = input.tree.scan(ray.clone(), bump_dist, dist) {
        // Loop limit check.
        if num_loops >= loop_limit {
            println!("[WARN] : Terminating shadower: loop limit reached.");
            return 0.0;
        }
        num_loops += 1;

        // Check if we've flown far enough.
        dist -= hit.dist();
        if dist < 0.0 {
            return vis;
        }

        // Check if it's still worth going.
        if vis < min_weight {
            return 0.0;
        }

        // Handle collision.
        match *hit.tag() {
            Attribute::Opaque(..) => {
                return vis / dist.mul_add(input.shader.fall_off(), 1.0);
            }
            Attribute::Mirror(.., abs_frac) => {
                ray.travel(dist);
                vis *= 1.0 - abs_frac;
                *ray.dir_mut() = Crossing::calc_ref_dir(ray.dir(), hit.side().norm());
                ray.travel(bump_dist);
            }
            Attribute::Transparent(.., abs_frac) => {
                ray.travel(dist + bump_dist);
                vis *= 1.0 - abs_frac;
            }
            Attribute::Refractive(.., abs_frac, [_inside, _outside]) => {
                ray.travel(dist + bump_dist);
                vis *= 1.0 - abs_frac;
            }
            Attribute::Luminous(.., bright_mult) => {
                return (vis * bright_mult) / dist.mul_add(input.shader.fall_off(), 1.0);
            }
        }
    }

    vis
}