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