aboutsummaryrefslogtreecommitdiff
path: root/src/core/transform.rs
blob: f64710c854bd02d6b97275299f4341b26ce67462 (plain)
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
121
122
123
124
125
126
127
128
129
//! Implements matrix tranformations
//!
//! Used for placing shapes and camera into the scene.
//! Provides some common initializations methods for transforms like rotation, translate and camera
//! placement.
//!
//! # Examples
//!
//! ```
//! use pathtrace::core::{Vector3f, Transform};
//!
//! let t = Transform::new_translate(3.0, 5.0, 6.0);
//! let v = t.eval_point(&Vector3f::new_xyz(1.0, 1.0, 1.0));
//!
//! assert!(v.x == 4.0 && v.y == 6.0 && v.z == 7.0);
//! ```
use super::matrix4x4::Matrix4x4f;
use crate::Float;
use crate::core::Vector3f;

pub struct Transform {
    m: Matrix4x4f,
}

impl Transform {
    pub fn new() -> Transform {
        Transform {
            m: Matrix4x4f::new_ident(1.0),
        }
    }

    /// Evaluation a point through the matrix
    pub fn eval_point(&self, p: &Vector3f) -> Vector3f {
        let m = &self.m.m;
        let x = m[0][0]*p.x + m[0][1]*p.y + m[0][2]*p.z + m[0][3];
        let y = m[1][0]*p.x + m[1][1]*p.y + m[1][2]*p.z + m[1][3];
        let z = m[2][0]*p.x + m[2][1]*p.y + m[2][2]*p.z + m[2][3];
        let w = m[3][0]*p.x + m[3][1]*p.y + m[3][2]*p.z + m[3][3];

        let mut out = Vector3f::new_xyz(x, y, z);
        if w != 1.0 {
            out /= w;
        }

        out
    }

    /// Evaluation of a vector
    ///
    /// This will not work for normal vectors as they become distorted
    pub fn eval_vector(&self, v: &Vector3f) -> Vector3f {
        let m = &self.m.m;
        let x = m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z;
        let y = m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z;
        let z = m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z;

        Vector3f::new_xyz(x, y, z)
    }
}

// Creation of different transformations
impl Transform {
    pub fn new_translate(x: Float, y: Float, z: Float) -> Self {
        Transform { m: Matrix4x4f::new(
                1.0, 0.0, 0.0, x,
                0.0, 1.0, 0.0, y,
                0.0, 0.0, 1.0, z,
                0.0, 0.0, 0.0, 1.0)
        }
    }

    pub fn new_scale(x: Float, y: Float, z: Float) -> Self {
        Transform { m: Matrix4x4f::new(
                x, 0.0, 0.0, 0.0,
                0.0, y, 0.0, 0.0,
                0.0, 0.0, z, 0.0,
                0.0, 0.0, 0.0, 1.0)
        }
    }

    pub fn new_rotate_x(theta: Float) -> Self {
        let theta = theta.to_radians();
        let cost = theta.cos();
        let sint = theta.sin();
        Transform { m: Matrix4x4f::new(
                1.0, 0.0, 0.0, 0.0,
                0.0, cost, -sint, 0.0,
                0.0, sint, cost, 0.0,
                0.0, 0.0, 0.0, 1.0)
        }
    }

    pub fn new_rotate_y(theta: Float) -> Self {
        let theta = theta.to_radians();
        let cost = theta.cos();
        let sint = theta.sin();
        Transform { m: Matrix4x4f::new(
                cost, 0.0, sint, 0.0,
                0.0, 1.0, 0.0, 0.0,
                -sint, 0.0, cost, 0.0,
                0.0, 0.0, 0.0, 1.0)
        }
    }

    pub fn new_rotate_z(theta: Float) -> Self {
        let theta = theta.to_radians();
        let cost = theta.cos();
        let sint = theta.sin();
        Transform { m: Matrix4x4f::new(
                cost, -sint, 0.0, 0.0,
                sint, cost, 0.0, 0.0,
                0.0, 0.0, 1.1, 0.0,
                0.0, 0.0, 0.0, 1.0)
        }
    }

    pub fn new_look_at(pos: &Vector3f, look: &Vector3f, up: &Vector3f) -> Self {
        let dir = (*look - *pos).norm();  // This is what the z axis should map to
        let right = up.norm().cross(&dir).norm();
        let newup = dir.cross(&right);

        Transform { m: Matrix4x4f::new(
                right.x, newup.x, dir.x, pos.x,
                right.y, newup.y, dir.y, pos.y,
                right.z, newup.z, dir.z, pos.z,
                0.0    , 0.0    , 0.0  , 1.0)
        }
    }
}