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
use crate::core::object::Object;
use crate::core::HitRecord;
use crate::core::ScatterRecord;
use crate::material::{Material, Visitor};
use crate::math::Ray;
use crate::texture::Texture;
use crate::types::{ColorRGB, FSize};
use std::error::Error;
use std::sync::Arc;

pub struct DiffuseLight {
    pub id: usize,
    pub emit: Arc<dyn Texture>,
}

impl DiffuseLight {
    pub fn new(emit: Arc<dyn Texture>) -> DiffuseLight {
        DiffuseLight {
            id: Object::new_id(),
            emit,
        }
    }
}

impl Material for DiffuseLight {
    fn get_id(&self) -> usize {
        self.id
    }

    fn scatter(
        &self,
        _self_material: Arc<dyn Material>,
        _ray_in: &Ray,
        _hit_record: &HitRecord,
    ) -> Option<ScatterRecord> {
        None
    }

    fn scattering_pdf(&self, _: &Ray, _: &HitRecord, _: &Ray) -> FSize {
        1.0
    }

    fn has_alpha(&self) -> bool {
        false
    }

    fn emitted(&self, ray_in: &Ray, hit_record: &HitRecord) -> ColorRGB {
        if glm::dot(ray_in.direction, hit_record.normal) < 0.0 {
            self.emit
                .value(&hit_record.uv, &hit_record.position)
                .truncate(3)
        } else {
            ColorRGB::new(0.0, 0.0, 0.0)
        }
    }

    fn accept(&self, visitor: &mut dyn Visitor) -> Result<(), Box<dyn Error>> {
        visitor.visit_diffuse_light(&self)
    }
}

#[cfg(test)]
mod diffuse_light_test {
    use super::*;
    use crate::material::NoMaterial;
    use crate::test;
    use crate::texture::ConstantTexture;
    use crate::types::{ColorRGBA, Point3, TextureCoordinate, Vector3};

    #[test]
    fn scatter_test() {
        let m = Arc::new(DiffuseLight::new(Arc::new(ConstantTexture::new(
            ColorRGBA::new(1.0, 0.0, 0.0, 1.0),
        ))));
        let result = m.scatter(
            m.clone(),
            &Ray::new_ray(Point3::new(1.0, 0.0, 0.0), Vector3::new(0.0, 0.0, -1.0)),
            &HitRecord::new(
                0.0,
                TextureCoordinate::from_uv(0.0, 0.0),
                Point3::new(0.0, 0.0, 0.0),
                Vector3::new(0.0, 0.0, 1.0),
                Arc::new(NoMaterial::new()),
            ),
        );
        match result {
            Some(_) => panic!("no result"),
            None => (),
        }
    }

    #[test]
    fn emitted_test() {
        let m = DiffuseLight::new(Arc::new(ConstantTexture::new(ColorRGBA::new(
            1.0, 0.0, 0.0, 1.0,
        ))));
        let c = m.emitted(
            &Ray::new_ray(Point3::new(1.0, 0.0, 0.0), Vector3::new(0.0, 0.0, -1.0)),
            &HitRecord::new(
                0.0,
                TextureCoordinate::from_uv(0.0, 0.0),
                Point3::new(0.0, 0.0, 0.0),
                Vector3::new(0.0, 0.0, 1.0),
                Arc::new(NoMaterial::new()),
            ),
        );
        test::assert_eq_vector3(&c, &ColorRGB::new(1.0, 0.0, 0.0), 0.01);
    }
}