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
use glam::{Mat4, Vec3};
/// Camera projection mode
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Projection {
/// Perspective projection with field of view
Perspective {
/// Vertical field of view in radians
fov_y_radians: f32,
},
/// Orthographic projection
Orthographic {
/// Viewport height in world units (width is calculated from aspect ratio)
height: f32,
},
}
impl Default for Projection {
fn default() -> Self {
Projection::Perspective {
fov_y_radians: std::f32::consts::FRAC_PI_4,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Camera3D {
pub eye: Vec3,
pub target: Vec3,
pub up: Vec3,
pub projection: Projection,
pub znear: f32,
pub zfar: f32,
}
impl Default for Camera3D {
fn default() -> Self {
Self {
eye: Vec3::new(0.0, -5.0, 3.0),
target: Vec3::ZERO,
up: Vec3::Y,
projection: Projection::default(),
znear: 0.1,
zfar: 100.0,
}
}
}
impl Camera3D {
/// Create a perspective camera.
///
/// # Arguments
/// * `eye` - Camera position
/// * `target` - Point the camera looks at
/// * `fov_y_radians` - Vertical field of view in radians
pub fn perspective(eye: Vec3, target: Vec3, fov_y_radians: f32) -> Self {
Self {
eye,
target,
up: Vec3::Y,
projection: Projection::Perspective { fov_y_radians },
znear: 0.1,
zfar: 100.0,
}
}
/// Create an orthographic camera.
///
/// # Arguments
/// * `eye` - Camera position
/// * `target` - Point the camera looks at
/// * `height` - Viewport height in world units
pub fn orthographic(eye: Vec3, target: Vec3, height: f32) -> Self {
Self {
eye,
target,
up: Vec3::Y,
projection: Projection::Orthographic { height },
znear: 0.1,
zfar: 100.0,
}
}
/// Get the vertical field of view in radians (only valid for perspective projection).
/// Returns default FOV for orthographic cameras.
pub fn fov_y_radians(&self) -> f32 {
match self.projection {
Projection::Perspective { fov_y_radians } => fov_y_radians,
Projection::Orthographic { .. } => std::f32::consts::FRAC_PI_4,
}
}
/// Set the vertical field of view (converts to perspective if needed).
pub fn set_fov_y_radians(&mut self, fov: f32) {
self.projection = Projection::Perspective { fov_y_radians: fov };
}
pub fn view_matrix(&self) -> Mat4 {
Mat4::look_at_rh(self.eye, self.target, self.up)
}
pub fn projection_matrix(&self, aspect: f32) -> Mat4 {
match self.projection {
Projection::Perspective { fov_y_radians } => {
// Using perspective_rh (wgpu [0,1] depth range) with inverted znear/zfar
// for reverse-Z depth buffer (bigger Z is closer), matching the 2D camera convention.
Mat4::perspective_rh(fov_y_radians, aspect, self.zfar, self.znear)
}
Projection::Orthographic { height } => {
let half_height = height / 2.0;
let half_width = half_height * aspect;
// Using orthographic_rh with inverted znear/zfar for reverse-Z
Mat4::orthographic_rh(
-half_width,
half_width,
-half_height,
half_height,
self.zfar,
self.znear,
)
}
}
}
pub fn view_proj(&self, aspect: f32) -> Mat4 {
self.projection_matrix(aspect) * self.view_matrix()
}
}