1use std::ops::{Add, Mul, Neg, Sub};
5
6#[derive(Debug, Clone, Copy, PartialEq, Default)]
7pub struct Vec3 {
8 pub x: f32,
9 pub y: f32,
10 pub z: f32,
11}
12
13impl Vec3 {
14 pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);
15 pub const ONE: Self = Self::new(1.0, 1.0, 1.0);
16
17 pub const fn new(x: f32, y: f32, z: f32) -> Self {
18 Self { x, y, z }
19 }
20
21 pub fn from_array(a: [f32; 3]) -> Self {
22 Self::new(a[0], a[1], a[2])
23 }
24
25 pub fn to_array(self) -> [f32; 3] {
26 [self.x, self.y, self.z]
27 }
28
29 pub fn dot(self, o: Self) -> f32 {
30 self.x * o.x + self.y * o.y + self.z * o.z
31 }
32
33 pub fn cross(self, o: Self) -> Self {
34 Self::new(self.y * o.z - self.z * o.y, self.z * o.x - self.x * o.z, self.x * o.y - self.y * o.x)
35 }
36
37 pub fn length(self) -> f32 {
38 self.dot(self).sqrt()
39 }
40
41 pub fn normalize(self) -> Self {
43 let l = self.length();
44 if l < 1e-6 { self } else { self * (1.0 / l) }
45 }
46
47 pub fn lerp(self, to: Self, t: f32) -> Self {
48 self + (to - self) * t
49 }
50
51 pub fn scale(self, o: Self) -> Self {
53 Self::new(self.x * o.x, self.y * o.y, self.z * o.z)
54 }
55}
56
57impl Add for Vec3 {
58 type Output = Self;
59 fn add(self, o: Self) -> Self {
60 Self::new(self.x + o.x, self.y + o.y, self.z + o.z)
61 }
62}
63
64impl Sub for Vec3 {
65 type Output = Self;
66 fn sub(self, o: Self) -> Self {
67 Self::new(self.x - o.x, self.y - o.y, self.z - o.z)
68 }
69}
70
71impl Mul<f32> for Vec3 {
72 type Output = Self;
73 fn mul(self, k: f32) -> Self {
74 Self::new(self.x * k, self.y * k, self.z * k)
75 }
76}
77
78impl Neg for Vec3 {
79 type Output = Self;
80 fn neg(self) -> Self {
81 Self::new(-self.x, -self.y, -self.z)
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq)]
87pub struct Mat4(pub [f32; 16]);
88
89impl Default for Mat4 {
90 fn default() -> Self {
91 Self::IDENTITY
92 }
93}
94
95impl Mat4 {
96 pub const IDENTITY: Self = Self([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]);
97
98 fn at(&self, row: usize, col: usize) -> f32 {
99 self.0[col * 4 + row]
100 }
101
102 pub fn translation(v: Vec3) -> Self {
103 let mut m = Self::IDENTITY;
104 m.0[12] = v.x;
105 m.0[13] = v.y;
106 m.0[14] = v.z;
107 m
108 }
109
110 pub fn scaling(v: Vec3) -> Self {
111 let mut m = Self::IDENTITY;
112 m.0[0] = v.x;
113 m.0[5] = v.y;
114 m.0[10] = v.z;
115 m
116 }
117
118 pub fn rotation_x(a: f32) -> Self {
120 let (s, c) = a.sin_cos();
121 Self([1.0, 0.0, 0.0, 0.0, 0.0, c, s, 0.0, 0.0, -s, c, 0.0, 0.0, 0.0, 0.0, 1.0])
122 }
123
124 pub fn rotation_y(a: f32) -> Self {
125 let (s, c) = a.sin_cos();
126 Self([c, 0.0, -s, 0.0, 0.0, 1.0, 0.0, 0.0, s, 0.0, c, 0.0, 0.0, 0.0, 0.0, 1.0])
127 }
128
129 pub fn rotation_z(a: f32) -> Self {
130 let (s, c) = a.sin_cos();
131 Self([c, s, 0.0, 0.0, -s, c, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
132 }
133
134 pub fn rotation_zyx(r: Vec3) -> Self {
137 Self::rotation_z(r.z) * Self::rotation_y(r.y) * Self::rotation_x(r.x)
138 }
139
140 pub fn part(pivot: Vec3, rotation: Vec3, scale: Vec3) -> Self {
142 Self::translation(pivot) * Self::rotation_zyx(rotation) * Self::scaling(scale)
143 }
144
145 pub fn transform_point(&self, p: Vec3) -> Vec3 {
146 Vec3::new(
147 self.at(0, 0) * p.x + self.at(0, 1) * p.y + self.at(0, 2) * p.z + self.at(0, 3),
148 self.at(1, 0) * p.x + self.at(1, 1) * p.y + self.at(1, 2) * p.z + self.at(1, 3),
149 self.at(2, 0) * p.x + self.at(2, 1) * p.y + self.at(2, 2) * p.z + self.at(2, 3),
150 )
151 }
152
153 pub fn transform_dir(&self, d: Vec3) -> Vec3 {
155 Vec3::new(
156 self.at(0, 0) * d.x + self.at(0, 1) * d.y + self.at(0, 2) * d.z,
157 self.at(1, 0) * d.x + self.at(1, 1) * d.y + self.at(1, 2) * d.z,
158 self.at(2, 0) * d.x + self.at(2, 1) * d.y + self.at(2, 2) * d.z,
159 )
160 }
161
162 pub fn transform_normal(&self, n: Vec3) -> Vec3 {
165 self.transform_dir(n).normalize()
166 }
167}
168
169impl Mul for Mat4 {
170 type Output = Self;
171 fn mul(self, o: Self) -> Self {
172 let mut r = [0.0; 16];
173 for col in 0..4 {
174 for row in 0..4 {
175 r[col * 4 + row] = (0..4).map(|k| self.at(row, k) * o.at(k, col)).sum();
176 }
177 }
178 Self(r)
179 }
180}
181
182pub fn rad(deg: Vec3) -> Vec3 {
184 deg * (std::f32::consts::PI / 180.0)
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 fn close(a: Vec3, b: Vec3) -> bool {
192 (a - b).length() < 1e-5
193 }
194
195 #[test]
196 fn rotations_are_right_handed() {
197 let half = std::f32::consts::FRAC_PI_2;
198 assert!(close(Mat4::rotation_x(half).transform_point(Vec3::new(0.0, 1.0, 0.0)), Vec3::new(0.0, 0.0, 1.0)));
199 assert!(close(Mat4::rotation_y(half).transform_point(Vec3::new(0.0, 0.0, 1.0)), Vec3::new(1.0, 0.0, 0.0)));
200 assert!(close(Mat4::rotation_z(half).transform_point(Vec3::new(1.0, 0.0, 0.0)), Vec3::new(0.0, 1.0, 0.0)));
201 }
202
203 #[test]
204 fn a_part_moves_after_turning() {
205 let m = Mat4::part(Vec3::new(5.0, 0.0, 0.0), Vec3::new(0.0, 0.0, std::f32::consts::FRAC_PI_2), Vec3::ONE);
206 assert!(close(m.transform_point(Vec3::new(1.0, 0.0, 0.0)), Vec3::new(5.0, 1.0, 0.0)));
207 }
208}