gl_utils/render/
viewport.rs

1//! A rectangular viewport and associated 2D and 3D cameras.
2
3use math_utils as math;
4use glium;
5
6use crate::{Camera2d, Camera3d};
7use crate::render::params;
8
9/// A viewport defined by a `glium::Rect` structure, with associated 2D and 3D
10/// cameras
11#[derive(Debug)]
12pub struct Viewport {
13  /// Describes the viewport position and size.
14  ///
15  /// `left` and `bottom` are the number of pixels between the left and bottom
16  /// of the screen with the left and bottom borders of the viewport rectangle.
17  rect     : glium::Rect,
18  camera2d : Option <Camera2d>,
19  camera3d : Option <Camera3d>
20}
21
22pub struct Builder {
23  rect            : glium::Rect,
24  /// Overrides `position_2d` and `zoom_2d`
25  camera_2d       : bool,
26  /// Overrides `orthographic_3d` and `pose_3d`
27  camera_3d       : bool,
28  /// Set 3D camera projection to orthographic with the given zoom
29  orthographic_3d : Option <f32>,
30  pose_3d         : Option <math::Pose3 <f32>>,
31  position_2d     : Option <math::Point2 <f32>>,
32  zoom_2d         : Option <f32>
33}
34
35impl Viewport {
36  /// Create a new viewport with the given `Rect`
37  ///
38  /// # Panics
39  ///
40  /// Width or height are zero:
41  ///
42  /// ```should_panic
43  /// # extern crate gl_utils;
44  /// # extern crate math_utils as math;
45  /// # extern crate glium;
46  /// # fn main () {
47  /// # use gl_utils::render::Viewport;
48  /// let mut viewport = Viewport::new (glium::Rect {
49  ///   left: 0, bottom: 0, width: 0, height: 0
50  /// });
51  /// # }
52  /// ```
53  ///
54  /// Width or height are greater than `u16::MAX` (65535):
55  ///
56  /// ```should_panic
57  /// # extern crate gl_utils;
58  /// # extern crate math_utils as math;
59  /// # extern crate glium;
60  /// # fn main () {
61  /// # use gl_utils::render::Viewport;
62  /// let mut viewport = Viewport::new (glium::Rect {
63  ///   left: 0, bottom: 0, width: 65536, height: 65536
64  /// });
65  /// # }
66  /// ```
67  pub fn new (rect : glium::Rect) -> Self {
68    assert!(rect.width  <= u16::MAX as u32);
69    assert!(rect.height <= u16::MAX as u32);
70    Viewport {
71      rect,
72      camera2d: Some (Camera2d::new (rect.width as u16, rect.height as u16)),
73      camera3d: Some (Camera3d::new (rect.width as u16, rect.height as u16))
74    }
75  }
76
77  /// Also initializes `self.camera3d` with position and orientation
78  pub fn with_pose_3d (
79    rect : glium::Rect,
80    pose : math::Pose3 <f32>
81  ) -> Self {
82    assert!(rect.width  <= u16::MAX as u32);
83    assert!(rect.height <= u16::MAX as u32);
84    Viewport {
85      rect,
86      camera2d: Some (Camera2d::new (rect.width as u16, rect.height as u16)),
87      camera3d: Some (Camera3d::with_pose (
88        rect.width as u16, rect.height as u16,
89        pose
90      ))
91    }
92  }
93
94  pub fn rect (&self) -> &glium::Rect {
95    &self.rect
96  }
97  pub fn camera2d (&self) -> Option <&Camera2d> {
98    self.camera2d.as_ref()
99  }
100  pub fn camera3d (&self) -> Option <&Camera3d> {
101    self.camera3d.as_ref()
102  }
103
104  /// Should be called when screen resolution changes.
105  ///
106  /// # Panics
107  ///
108  /// Width and height must be less than or equal to `u16::MAX` (65535):
109  ///
110  /// ```should_panic
111  /// # extern crate gl_utils;
112  /// # extern crate math_utils as math;
113  /// # extern crate glium;
114  /// # fn main () {
115  /// # use gl_utils::render::Viewport;
116  /// # let mut viewport = Viewport::new (glium::Rect {
117  /// #   left: 0, bottom: 0, width: 320, height: 240
118  /// # });
119  /// viewport.set_rect (glium::Rect {
120  ///   left:   0,       bottom: 0,
121  ///   width:  100_000, height: 100_000 // panics
122  /// });
123  /// # }
124  /// ```
125  ///
126  /// Width or height are zero:
127  ///
128  /// ```should_panic
129  /// # extern crate gl_utils;
130  /// # extern crate math_utils as math;
131  /// # extern crate glium;
132  /// # fn main () {
133  /// # use gl_utils::render::Viewport;
134  /// # let mut viewport = Viewport::new (glium::Rect {
135  /// #   left: 0, bottom: 0, width: 320, height: 240
136  /// # });
137  /// viewport.set_rect (glium::Rect {
138  ///   left:   0, bottom: 0,
139  ///   width:  0, height: 0 // panics
140  /// });
141  /// # }
142  /// ```
143  pub fn set_rect (&mut self, rect : glium::Rect) {
144    assert!(rect.width  <= u16::MAX as u32);
145    assert!(rect.height <= u16::MAX as u32);
146    self.rect = rect;
147    self.camera2d.as_mut().map (|camera2d|
148      camera2d.set_viewport_dimensions (rect.width as u16, rect.height as u16));
149    self.camera3d.as_mut().map (|camera3d|
150      camera3d.set_viewport_dimensions (rect.width as u16, rect.height as u16));
151  }
152
153  pub fn camera2d_set_position (&mut self, position : math::Point2 <f32>) {
154    self.camera2d.as_mut().unwrap().set_position (position)
155  }
156  pub fn camera2d_set_zoom (&mut self, zoom : f32) {
157    self.camera2d.as_mut().unwrap().set_zoom (zoom)
158  }
159  pub fn camera2d_move_local (&mut self, dx : f32, dy : f32) {
160    self.camera2d.as_mut().unwrap().move_local (dx, dy)
161  }
162  pub fn camera2d_move_origin_to_bottom_left (&mut self) {
163    self.camera2d.as_mut().unwrap().move_origin_to_bottom_left()
164  }
165  pub fn camera3d_set_position (&mut self, position : math::Point3 <f32>) {
166    self.camera3d.as_mut().unwrap().set_position (position)
167  }
168  pub fn camera3d_set_orientation (&mut self,
169    orientation : math::Rotation3 <f32>
170  ) {
171    self.camera3d.as_mut().unwrap().set_orientation (orientation)
172  }
173  pub fn camera3d_look_at (&mut self, target : math::Point3 <f32>) {
174    self.camera3d.as_mut().unwrap().look_at (target)
175  }
176  pub fn camera3d_move_local_xy (&mut self, dx : f32, dy : f32, dz : f32) {
177    self.camera3d.as_mut().unwrap().move_local_xy (dx, dy, dz)
178  }
179  pub fn camera3d_rotate (&mut self,
180    dyaw : math::Rad <f32>, dpitch : math::Rad <f32>, droll : math::Rad <f32>
181  ) {
182    self.camera3d.as_mut().unwrap().rotate (dyaw, dpitch, droll)
183  }
184  pub fn camera3d_scale_fovy_or_zoom (&mut self, zoom : f32) {
185    self.camera3d.as_mut().unwrap().scale_fovy_or_zoom (zoom)
186  }
187  /// Returns default glium draw parameters with the viewport set to the current
188  /// rectangle
189  #[allow(mismatched_lifetime_syntaxes)]
190  pub fn draw_parameters (&self) -> glium::DrawParameters {
191    glium::DrawParameters {
192      viewport: Some (self.rect),
193      .. Default::default()
194    }
195  }
196
197  /// Draw parameters that will result in 'inverted' colors (used by tile
198  /// renderer)
199  #[allow(mismatched_lifetime_syntaxes)]
200  pub fn draw_parameters_blend_invert (&self) -> glium::DrawParameters {
201    glium::DrawParameters {
202      //blend: params::BLEND_FUNC_NORMAL,
203      // NOTE: inverse blending does not really have an effect when rendering
204      // with 'Nearest' magnify filter below
205      blend: params::BLEND_FUNC_INVERT_COLOR,
206      .. self.draw_parameters()
207    }
208  }
209}
210
211impl Builder {
212  #[inline]
213  pub fn new (rect : glium::Rect) -> Self {
214    Builder {
215      rect,
216      camera_2d:       true,
217      camera_3d:       true,
218      orthographic_3d: None,
219      pose_3d:         None,
220      position_2d:     None,
221      zoom_2d:         None
222    }
223  }
224  pub fn with_camera_2d (self, camera_2d : bool) -> Self {
225    Builder { camera_2d, .. self }
226  }
227  pub fn with_camera_3d (self, camera_3d : bool) -> Self {
228    Builder { camera_3d, .. self }
229  }
230  pub fn with_zoom_2d (self, zoom : f32) -> Self {
231    Builder { zoom_2d: Some (zoom), .. self }
232  }
233  pub fn with_position_2d (self, position_2d : math::Point2 <f32>) -> Self {
234    Builder { position_2d: Some (position_2d), .. self }
235  }
236  /// Changes the 3D camera projection from perspective (default) to orthographic
237  pub fn orthographic_3d (self, zoom : f32) -> Self {
238    Builder { orthographic_3d: Some (zoom), .. self }
239  }
240  pub fn with_pose_3d (self, pose_3d : math::Pose3 <f32>) -> Self {
241    Builder { pose_3d: Some (pose_3d), .. self }
242  }
243  #[inline]
244  pub fn build (self) -> Viewport {
245    let mut viewport = if let Some (pose_3d) = self.pose_3d {
246      Viewport::with_pose_3d (self.rect, pose_3d)
247    } else {
248      Viewport::new (self.rect)
249    };
250    if self.camera_2d {
251      if let Some (position) = self.position_2d {
252        viewport.camera2d.as_mut().unwrap().set_position (position);
253      }
254      if let Some (zoom) = self.zoom_2d {
255        viewport.camera2d.as_mut().unwrap().set_zoom (zoom);
256      }
257    } else {
258      let _ = viewport.camera2d.take();
259    }
260    if self.camera_3d {
261      if let Some (zoom) = self.orthographic_3d {
262        viewport.camera3d.as_mut().unwrap().to_orthographic (zoom);
263      }
264    } else {
265      let _ = viewport.camera3d.take();
266    }
267    viewport
268  }
269}
270
271#[cfg(test)]
272mod tests {
273  use super::*;
274  use glium;
275
276  #[test]
277  fn viewport_matrices() {
278    let viewport = Viewport::new (
279      glium::Rect { left: 0, bottom: 0, width: 1600, height: 900 }
280    );
281    let (transform_view_matrix, projection_ortho_matrix) =
282      viewport.camera2d().unwrap().view_ortho_mats();
283    println!("transform view matrix:\n{:#?}", transform_view_matrix);
284    println!("projection ortho matrix:\n{:#?}", projection_ortho_matrix);
285  }
286}