gl_utils/render/
viewport.rs

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