aboutsummaryrefslogtreecommitdiff
path: root/src/core/transform.rs
blob: 337d9b1aa6a146335383d6dfc48cdea7e254793c (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! 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;
use std::ops;

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)
    }

    pub fn inverse(&self) -> Self {
        Transform {
            m: self.m.inverse(),
        }
    }
}

impl ops::Mul for Transform {
    type Output = Transform;

    fn mul(self, op: Self) -> Self::Output {
        Transform {
            m: &self.m * &op.m
        }
    }
}

// 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)
        }
    }
}