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
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! Smooth triangle implementation.

use crate::{
    access,
    geom::{Collide, Cube, Emit, Ray, Side, Trace, Transformable, Triangle},
    math::{Dir3, Pos3, Trans3},
    ord::{ALPHA, BETA, GAMMA},
};
use rand::Rng;

/// Triangle geometry with normal interpolation.
pub struct SmoothTriangle {
    /// Base triangle.
    tri: Triangle,
    /// Normal vectors.
    norms: [Dir3; 3],
}

impl SmoothTriangle {
    access!(tri, Triangle);
    access!(norms, [Dir3; 3]);

    /// Construct a new instance.
    #[inline]
    #[must_use]
    pub fn new(tri: Triangle, norms: [Dir3; 3]) -> Self {
        if !norms.iter().all(|&n| n.dot(tri.plane_norm()) > 0.0) {
            println!("[WARN] Reverse triangle.");
        }

        Self { tri, norms }
    }

    /// Construct a new instance from vertices.
    #[inline]
    #[must_use]
    pub fn new_from_verts(verts: [Pos3; 3], norms: [Dir3; 3]) -> Self {
        Self::new(Triangle::new(verts), norms)
    }
}

impl Collide for SmoothTriangle {
    #[inline]
    #[must_use]
    fn overlap(&self, cube: &Cube) -> bool {
        self.tri.overlap(cube)
    }
}

impl Trace for SmoothTriangle {
    #[inline]
    #[must_use]
    fn hit(&self, ray: &Ray) -> bool {
        self.tri.hit(ray)
    }

    #[inline]
    #[must_use]
    fn dist(&self, ray: &Ray) -> Option<f64> {
        self.tri.dist(ray)
    }

    #[inline]
    #[must_use]
    fn dist_side(&self, ray: &Ray) -> Option<(f64, Side)> {
        if let Some((dist, [u, v, w])) = self.tri.intersection_coors(ray) {
            Some((
                dist,
                Side::new(
                    ray.dir(),
                    Dir3::new_normalize(
                        (self.norms[BETA].into_inner() * u)
                            + (self.norms[GAMMA].into_inner() * v)
                            + (self.norms[ALPHA].into_inner() * w),
                    ),
                ),
            ))
        } else {
            None
        }
    }
}

impl Transformable for SmoothTriangle {
    #[inline]
    fn transform(&mut self, trans: &Trans3) {
        self.tri.transform(trans);

        for n in &mut self.norms {
            *n = Dir3::new_normalize(trans.transform_vector(n.as_ref()));
        }
    }
}

impl Emit for SmoothTriangle {
    #[inline]
    #[must_use]
    fn cast<R: Rng>(&self, rng: &mut R) -> Ray {
        let mut u = rng.gen::<f64>();
        let mut v = rng.gen::<f64>();

        if (u + v) > 1.0 {
            u = 1.0 - u;
            v = 1.0 - v;
        }
        let w = 1.0 - u - v;

        let edge_a_b = self.tri.verts()[BETA] - self.tri.verts()[ALPHA];
        let edge_a_c = self.tri.verts()[GAMMA] - self.tri.verts()[ALPHA];

        let pos = self.tri.verts()[ALPHA] + (edge_a_b * u) + (edge_a_c * v);
        let dir = Dir3::new_normalize(
            (self.norms[BETA].as_ref() * u)
                + (self.norms[GAMMA].as_ref() * v)
                + (self.norms[ALPHA].as_ref() * w),
        );

        Ray::new(pos, dir)
    }
}