Skip to main content

renderling/camera/
cpu.rs

1//! CPU side of [crate::camera].
2
3use craballoc::{runtime::IsRuntime, slab::SlabAllocator, value::Hybrid};
4use crabslab::Id;
5
6use crate::camera::shader::CameraDescriptor;
7
8use super::*;
9
10/// A camera used for transforming the stage during rendering.
11///
12/// * Use [`Stage::new_camera`](crate::stage::Stage::new_camera) to create a new
13///   camera.
14/// * Use [`Stage::use_camera`](crate::stage::Stage::use_camera) to set a camera
15///   on the stage.
16///
17/// ## Note
18///
19/// Clones of this type all point to the same underlying data.
20#[derive(Clone, Debug)]
21pub struct Camera {
22    inner: Hybrid<CameraDescriptor>,
23}
24
25impl AsRef<Camera> for Camera {
26    fn as_ref(&self) -> &Camera {
27        self
28    }
29}
30
31impl Camera {
32    /// Stage a new camera on the given slab.
33    pub fn new(slab: &SlabAllocator<impl IsRuntime>) -> Self {
34        Self {
35            inner: slab.new_value(CameraDescriptor::default()),
36        }
37    }
38
39    /// Returns a pointer to the underlying descriptor on the GPU.
40    pub fn id(&self) -> Id<CameraDescriptor> {
41        self.inner.id()
42    }
43
44    /// Returns a copy of the underlying descriptor.
45    pub fn descriptor(&self) -> CameraDescriptor {
46        self.inner.get()
47    }
48
49    /// Set the camera to a default perspective projection and view based
50    /// on the width and height of the viewport.
51    ///
52    /// The default projection and view matrices are defined as:
53    ///
54    /// ```rust
55    /// use glam::*;
56    ///
57    /// let width = 800.0;
58    /// let height = 600.0;
59    /// let aspect = width / height;
60    /// let fovy = core::f32::consts::PI / 4.0;
61    /// let znear = 0.1;
62    /// let zfar = 100.0;
63    /// let projection = Mat4::perspective_rh(fovy, aspect, znear, zfar);
64    /// let eye = Vec3::new(0.0, 12.0, 20.0);
65    /// let target = Vec3::ZERO;
66    /// let up = Vec3::Y;
67    /// let view = Mat4::look_at_rh(eye, target, up);
68    /// assert_eq!(
69    ///     renderling::camera::default_perspective(width, height),
70    ///     (projection, view)
71    /// );
72    /// ```
73    pub fn set_default_perspective(&self, width: f32, height: f32) -> &Self {
74        self.inner
75            .modify(|d| *d = CameraDescriptor::default_perspective(width, height));
76        self
77    }
78
79    /// Set the camera to a default perspective projection and view based
80    /// on the width and height of the viewport.
81    ///
82    /// The default projection and view matrices are defined as:
83    ///
84    /// ```rust
85    /// use glam::*;
86    ///
87    /// let width = 800.0;
88    /// let height = 600.0;
89    /// let aspect = width / height;
90    /// let fovy = core::f32::consts::PI / 4.0;
91    /// let znear = 0.1;
92    /// let zfar = 100.0;
93    /// let projection = Mat4::perspective_rh(fovy, aspect, znear, zfar);
94    /// let eye = Vec3::new(0.0, 12.0, 20.0);
95    /// let target = Vec3::ZERO;
96    /// let up = Vec3::Y;
97    /// let view = Mat4::look_at_rh(eye, target, up);
98    /// assert_eq!(
99    ///     renderling::camera::default_perspective(width, height),
100    ///     (projection, view)
101    /// );
102    /// ```
103    pub fn with_default_perspective(self, width: f32, height: f32) -> Self {
104        self.set_default_perspective(width, height);
105        self
106    }
107
108    /// Set the camera to a default orthographic 2d projection and view based
109    /// on the width and height of the viewport.
110    pub fn set_default_ortho2d(&self, width: f32, height: f32) -> &Self {
111        self.inner
112            .modify(|d| *d = CameraDescriptor::default_ortho2d(width, height));
113        self
114    }
115
116    /// Set the camera to a default orthographic 2d projection and view based
117    /// on the width and height of the viewport.
118    pub fn with_default_ortho2d(self, width: f32, height: f32) -> Self {
119        self.set_default_ortho2d(width, height);
120        self
121    }
122
123    /// Set the projection and view matrices of this camera.
124    pub fn set_projection_and_view(
125        &self,
126        projection: impl Into<Mat4>,
127        view: impl Into<Mat4>,
128    ) -> &Self {
129        self.inner
130            .modify(|d| d.set_projection_and_view(projection.into(), view.into()));
131        self
132    }
133
134    /// Set the projection and view matrices and return this camera.
135    pub fn with_projection_and_view(
136        self,
137        projection: impl Into<Mat4>,
138        view: impl Into<Mat4>,
139    ) -> Self {
140        self.set_projection_and_view(projection, view);
141        self
142    }
143
144    /// Returns the projection and view matrices.
145    pub fn projection_and_view(&self) -> (Mat4, Mat4) {
146        let d = self.inner.get();
147        (d.projection(), d.view())
148    }
149
150    /// Set the projection matrix of this camera.
151    pub fn set_projection(&self, projection: impl Into<Mat4>) -> &Self {
152        self.inner.modify(|d| d.set_projection(projection.into()));
153        self
154    }
155
156    /// Set the projection matrix and return this camera.
157    pub fn with_projection(self, projection: impl Into<Mat4>) -> Self {
158        self.set_projection(projection);
159        self
160    }
161
162    /// Returns the projection matrix.
163    pub fn projection(&self) -> Mat4 {
164        self.inner.get().projection()
165    }
166
167    /// Set the view matrix of this camera.
168    pub fn set_view(&self, view: impl Into<Mat4>) -> &Self {
169        self.inner.modify(|d| d.set_view(view.into()));
170        self
171    }
172
173    /// Set the view matrix and return this camera.
174    pub fn with_view(self, view: impl Into<Mat4>) -> Self {
175        self.set_view(view);
176        self
177    }
178
179    /// Returns the view matrix.
180    pub fn view(&self) -> Mat4 {
181        self.inner.get().view()
182    }
183}
184
185#[cfg(test)]
186mod test {
187    use craballoc::{runtime::CpuRuntime, slab::SlabAllocator};
188
189    use super::*;
190
191    #[test]
192    fn camera_position_sanity() {
193        let slab = SlabAllocator::new(CpuRuntime, "camera test", ());
194        let camera = Camera::new(&slab);
195        let projection = Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, 1.0, 0.01, 10.0);
196        let view = Mat4::look_at_rh(Vec3::ONE, Vec3::ZERO, Vec3::Y);
197        camera.set_projection_and_view(projection, view);
198        let position = camera.descriptor().position();
199        assert_eq!(Vec3::ONE, position);
200    }
201}