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
use cgmath::{self, Ortho, PerspectiveFov, Point3};
use maths::{Matrix4f, Vector2f, Vector2u, Vector3f};
/// Different ways to calculate camera width and height from `size`.
pub enum CameraScaleMode {
/// `size` will always be width/horizontal FOV.
Width,
/// `size` will always be height/vertical FOV.
Height,
/// `size` will be width if width < height, and vice versa.
Min,
/// `size` will be width if width > height, and vice versa.
Max,
}
/// Represents a 3D projection and view.
///
/// Required for drawing sprites/meshes.
pub struct Camera {
/// Position of the camera in world space.
pub position: Vector3f,
/// Direction the camera is looking towards.
pub direction: Vector3f,
/// Near plane distance.
pub near: f32,
/// Far plane distance.
pub far: f32,
/// Size represents the frustum size if the camera is orthographic,
///
/// and FOV if the camera is perspective.
pub size: f32,
/// Determines how the width and height of the camera will be calculated from `size` and window size.
pub scale_mode: CameraScaleMode,
/// Whether the camera is perspective or orthographic.
pub perspective: bool,
}
impl Camera {
/// Creates a new camera.
///
/// Same as struct initialization.
pub fn new(
position: Vector3f,
direction: Vector3f,
near: f32,
far: f32,
size: f32,
scale_mode: CameraScaleMode,
perspective: bool,
) -> Self {
Self {
position,
direction,
near,
far,
size,
scale_mode,
perspective,
}
}
/// Make camera look at a point from its current position.
pub fn look_at(&mut self, target: Vector3f) {
self.direction = target - self.position;
}
/// Combined projection and view matrices.
pub fn matrix(&self, window_size: Vector2u) -> Matrix4f {
self.proj_matrix(window_size) * self.view_matrix()
}
/// Projection matrix.
pub fn proj_matrix(&self, window_size: Vector2u) -> Matrix4f {
let ratio = window_size.x as f32 / window_size.y as f32;
let size: Vector2f = match self.scale_mode {
CameraScaleMode::Width => Vector2f::new(self.size, self.size / ratio),
CameraScaleMode::Height => Vector2f::new(self.size * ratio, self.size),
CameraScaleMode::Min => if ratio < 1.0 {
Vector2f::new(self.size, self.size / ratio)
} else {
Vector2f::new(self.size * ratio, self.size)
},
CameraScaleMode::Max => if ratio > 1.0 {
Vector2f::new(self.size, self.size / ratio)
} else {
Vector2f::new(self.size * ratio, self.size)
},
};
if self.perspective {
PerspectiveFov {
fovy: cgmath::Deg(size.y).into(),
near: self.near,
far: self.far,
aspect: size.x / size.y,
}.into()
} else {
Ortho {
left: -size.x / 2.0,
right: size.x / 2.0,
bottom: -size.y / 2.0,
top: size.y / 2.0,
near: self.near,
far: self.far,
}.into()
}
}
/// View matrix.
pub fn view_matrix(&self) -> Matrix4f {
let pos = Point3::new(self.position.x, self.position.y, self.position.z);
Matrix4f::look_at_dir(pos, self.direction, Vector3f::new(0.0, 1.0, 0.0))
}
}