gl_utils/render/resource/
demo.rs

1use std::sync::{Arc, RwLock};
2use lazy_static::lazy_static;
3use strum::IntoEnumIterator;
4use winit;
5
6use math_utils as math;
7use math_utils::num_traits as num;
8
9use crate::{tile, Render};
10use super::*;
11
12/// A type to implement winit ApplicationHandler trait
13pub struct App {
14  pub render             : Render <Default>,
15  pub mouse_position     : (f64, f64),
16  pub mouse_button_event : Option <winit::event::ElementState>,
17  pub running            : bool
18}
19
20impl App {
21  pub fn new (
22    glium_display : glium::Display <glutin::surface::WindowSurface>,
23    window        : winit::window::Window
24  ) -> Self {
25    let mut render = Render::<Default>::new (glium_display, window);
26    render.demo_init();
27    let mouse_position      = (0.0, 0.0);
28    let mouse_button_event  = None;
29    let running             = true;
30    Self {
31      render, mouse_position, mouse_button_event, running
32    }
33  }
34}
35
36impl winit::application::ApplicationHandler for App {
37  // required
38  fn resumed (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) { }
39
40  fn window_event (&mut self,
41    _event_loop : &winit::event_loop::ActiveEventLoop,
42    _window_id  : winit::window::WindowId,
43    event       : winit::event::WindowEvent
44  ) {
45    // winit events:
46    // TODO: review
47    // - window events includes input events received by the window
48    // - device events are received independent of window focus
49    // there may be differences in the events received depending on platform:
50    // - on Linux keyboard input is received as both a device and window event
51    // - on Windows only mouse motion device events are received, all other
52    //   input is received as window events
53    self.render.demo_handle_winit_window_event (
54      event,
55      &mut self.running,
56      &mut self.mouse_position,
57      &mut self.mouse_button_event)
58  }
59}
60
61impl Render <Default> {
62  /// Initializes "demo" state such as example mesh instances and viewport text
63  /// tiles.
64  ///
65  /// This should normally be called immediately after render context creation
66  /// as some assumptions are made about the current state (see Panics below).
67  /// The renderer can be returned to this state with the `reset` method.
68  ///
69  /// After `demo_init`, `demo_handle_winit_event` can be called on incoming
70  /// events to interact with the demo.
71  ///
72  /// A usage example is provided in `./examples/demo.rs`.
73  ///
74  /// # Panics
75  ///
76  /// A prerequisite is that the first four `viewport_resources` entries are
77  /// empty.
78  ///
79  /// The currently allocated per-instance buffers should be empty.
80  // TODO: doctests, make safe to call from any state?
81  fn demo_init (&mut self) {
82    use draw3d::*;
83
84    println!(">>> Initializing gl-utils demo...");
85    println!("  Press 'Q' or 'Esc' to quit");
86    println!("  Horizontal movement: 'W', 'A', 'S', 'D'");
87    println!("  Vertical movement: 'Space', 'LCtrl' or 'R', 'F'");
88    println!("  Rotation: 'Up', 'Down', 'Left', 'Right' or 'H', 'J', 'K', 'L'");
89    println!("  Zoom in/out 3D (perspective): 'I', 'O'");
90    println!("  Zoom in/out 3D (orthographic): 'Alt+I', 'Alt+O'");
91    println!("  Zoom in/out 2D: 'Shift+Alt+I', 'Shift+Alt+O'");
92    println!("  Toggle split into 4 viewports with orthographic views: 'X'");
93    println!("  Screenshot: 'F10'");
94
95    // set pointer texture
96    self.resource.draw2d.draw_pointer =
97      Some (DefaultTexturePointerId::Hand as PointerTextureIndexRepr);
98    // hide hardware cursor
99    self.window.set_cursor_visible (false);
100
101    //
102    // 3D per-instance data
103    //
104    // aabb lines vertex data
105    let aabb_lines_vertex = [vertex::Vert3dOrientationScaleColor {
106      position:    [-0.5, 2.5, 1.0],
107      orientation: math::Matrix3::identity().into_col_arrays(),
108      scale:       [0.5, 0.5, 1.0],
109      color:       color::rgba_u8_to_rgba_f32 (color::DEBUG_PINK)
110    }];
111    // aabb triangles vertex data
112    let aabb_triangles_vertex = [vertex::Vert3dOrientationScaleColor {
113      color: color::rgba_u8_to_rgba_f32 (color::DEBUG_GREY),
114      .. aabb_lines_vertex[0]
115    }];
116    // grid vertex data
117    let grid_vertex_data = Default::debug_grid_vertices();
118    // hemisphere vertex data
119    let hemisphere_vertex = [vertex::Vert3dOrientationScaleColor {
120      position:    [2.5, 2.5, 0.0],
121      orientation: math::Matrix3::identity().into_col_arrays(),
122      scale:       [0.5, 0.5, 0.5],
123      color:       color::rgba_u8_to_rgba_f32 (color::DEBUG_VIOLET)
124    }];
125    // sphere vertex data
126    // sphere: 1.0 diameter
127    let sphere_vertex = [vertex::Vert3dOrientationScaleColor {
128      position:    [1.5, 2.5, 0.5],
129      orientation: math::Matrix3::identity().into_col_arrays(),
130      scale:       [0.5, 0.5, 0.5],
131      color:       color::rgba_u8_to_rgba_f32 (color::DEBUG_RED)
132    }];
133    // capsule vertex data
134    let capsule_vertex = [vertex::Vert3dOrientationScaleColor {
135      position:    [0.5, 2.5, 1.0],
136      orientation: math::Matrix3::identity().into_col_arrays(),
137      scale:       [0.5, 0.5, 1.0],
138      color:       color::rgba_u8_to_rgba_f32 (color::DEBUG_AZURE)
139    }];
140    // cylinder vertex data
141    let cylinder_vertex = [vertex::Vert3dOrientationScaleColor {
142      position:    [3.5, 2.5, 1.0],
143      orientation: math::Matrix3::identity().into_col_arrays(),
144      scale:       [0.5, 0.5, 1.0],
145      color:       color::rgba_u8_to_rgba_f32 (color::DEBUG_LIGHT_BLUE)
146    }];
147    let aabb_lines               = Some (&aabb_lines_vertex[..]);
148    let aabb_triangles           = Some (&aabb_triangles_vertex[..]);
149    let aabb_lines_and_triangles = None;
150    let meshes = {
151      let mut v = VecMap::with_capacity (MeshId::COUNT);
152      assert!(v.insert (MeshId::Grid       as usize, &grid_vertex_data[..])
153        .is_none());
154      assert!(v.insert (MeshId::Hemisphere as usize, &hemisphere_vertex[..])
155        .is_none());
156      assert!(v.insert (MeshId::Sphere     as usize, &sphere_vertex[..])
157        .is_none());
158      assert!(v.insert (MeshId::Capsule    as usize, &capsule_vertex[..])
159        .is_none());
160      assert!(v.insert (MeshId::Cylinder   as usize, &cylinder_vertex[..])
161        .is_none());
162      v
163    };
164    // note: no billboard instances are created here, instead they are assigned
165    // to share instances with meshes
166    let billboards = VecMap::new();
167    self.resource.draw3d.instance_vertices_set (
168      &self.glium_display,
169      draw3d::InstancesInit {
170        aabb_lines, aabb_triangles, aabb_lines_and_triangles, meshes, billboards
171      }
172    );
173    // tile billboards
174    for mesh_id in MeshId::iter() {
175      let key = mesh_id as usize;
176      self.resource.draw3d.tile_billboard_create (
177        &self.glium_display, &self.resource.tileset_128x128_texture,
178        key, self.resource.draw3d.instanced_meshes()[key].instances_range.clone(),
179        format!("{mesh_id:?}").as_str()
180      );
181    }
182    let aabb_billboard_key = MeshId::COUNT;
183    self.resource.draw3d.tile_billboard_create (
184      &self.glium_display, &self.resource.tileset_128x128_texture,
185      aabb_billboard_key,  // start immediately after mesh billboards
186      self.resource.draw3d.instanced_aabb_lines().clone(),
187      "Aabb"
188    );
189
190    let (width, height) = self.window.inner_size().into();
191    let [_tile_width, tile_height] =
192      self.resource.tile_dimensions (DefaultTilesetId::EasciiAcorn128);
193    //
194    // 2D per-viewport tiles
195    //
196    let mut tile_2d_vertices = tile::vertices ("Viewport0", (0, 0));
197    let viewport0_tile_range = 0..tile_2d_vertices.len() as u32;
198    tile_2d_vertices.extend (tile::vertices ("Viewport1: +Y", (0, 0)));
199    let viewport1_tile_range = viewport0_tile_range.end..tile_2d_vertices.len()
200      as u32;
201    tile_2d_vertices.extend (tile::vertices ("Viewport2: +X", (0, 0)));
202    let viewport2_tile_range = viewport1_tile_range.end..tile_2d_vertices.len()
203      as u32;
204    tile_2d_vertices.extend (tile::vertices ("Viewport3: -Z", (0, 0)));
205    let viewport3_tile_range = viewport2_tile_range.end..tile_2d_vertices.len()
206      as u32;
207    //let center_tile = ((width / tile_width) / 2) as i32;
208    let fps_row = ((height / tile_height) - 1) as i32;
209    tile_2d_vertices.extend (tile::vertices ("FPS: 0     ", (fps_row, 0)));
210    let viewport4_tile_range = viewport3_tile_range.end..tile_2d_vertices.len()
211      as u32;
212    self.resource.draw2d.tile_2d_vertices = glium::VertexBuffer::dynamic (
213      &self.glium_display, &tile_2d_vertices[..]
214    ).unwrap();
215    let tile_color_2d_vertices = {
216      let tiles  = tile::vertices ("abc123",    (2, 0));
217      let colors = vec![
218        ( [1.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
219        ( [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
220        ( [0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
221        ( [0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 0.0, 1.0] ),
222        ( [0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 1.0, 1.0] ),
223        ( [0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 1.0] )
224      ];
225      tiles.into_iter().zip (colors).map (
226        |(vertex::Vert2dTile { tile, row, column }, (fg, bg))|
227        vertex::Vert2dTileColor { tile, row, column, fg, bg }
228      ).collect::<Vec <_>>()
229    };
230    let viewport0_tile_color_range = 0..tile_color_2d_vertices.len() as u32;
231    self.resource.draw2d.tile_color_2d_vertices = glium::VertexBuffer::dynamic (
232      &self.glium_display, &tile_color_2d_vertices[..]
233    ).unwrap();
234    let draw_tiles = draw2d::Tiles {
235      vertex_range: 0..0, origin: (2, 2).into(),
236      tileset_id: DefaultTilesetId::EasciiAcorn128
237    };
238    let draw_tiles_color = draw2d::Tiles {
239      vertex_range: 0..0, origin: draw2d::TilesOrigin::World,
240      tileset_id: DefaultTilesetId::EasciiAcorn256
241    };
242    self.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT,
243      draw2d::ViewportResources {
244        draw_indices: vec![draw2d::DrawIndices {
245          rectangle:        None,
246          draw_tiles:       Some (draw2d::Tiles {
247            vertex_range: viewport0_tile_range,
248            .. draw_tiles.clone()
249          }),
250          draw_color_tiles: Some (draw2d::Tiles {
251            vertex_range: viewport0_tile_color_range,
252            .. draw_tiles_color
253          })
254        }],
255        .. draw2d::ViewportResources::default()
256      }
257    );
258    self.resource.draw2d.viewport_resources_set (UPPER_LEFT_VIEWPORT,
259      draw2d::ViewportResources {
260        draw_indices: vec![draw2d::DrawIndices {
261          draw_tiles: Some (draw2d::Tiles {
262            vertex_range: viewport1_tile_range,
263            .. draw_tiles.clone()
264          }),
265          .. draw2d::DrawIndices::default()
266        }],
267        .. draw2d::ViewportResources::default()
268      }
269    );
270    self.resource.draw2d.viewport_resources_set (UPPER_RIGHT_VIEWPORT,
271      draw2d::ViewportResources {
272        draw_indices: vec![draw2d::DrawIndices {
273          draw_tiles: Some (draw2d::Tiles {
274            vertex_range: viewport2_tile_range,
275            .. draw_tiles.clone()
276          }),
277          .. draw2d::DrawIndices::default()
278        }],
279        .. draw2d::ViewportResources::default()
280      }
281    );
282    self.resource.draw2d.viewport_resources_set (LOWER_LEFT_VIEWPORT,
283      draw2d::ViewportResources {
284        draw_indices: vec![draw2d::DrawIndices {
285          draw_tiles: Some (draw2d::Tiles {
286            vertex_range: viewport3_tile_range,
287            .. draw_tiles.clone()
288          }),
289          .. draw2d::DrawIndices::default()
290        }],
291        .. draw2d::ViewportResources::default()
292      }
293    );
294    self.resource.draw2d.viewport_resources_set (OVERLAY_VIEWPORT,
295      draw2d::ViewportResources {
296        draw_indices: vec![draw2d::DrawIndices {
297          draw_tiles: Some (draw2d::Tiles {
298            vertex_range: viewport4_tile_range,
299            .. draw_tiles.clone()
300          }),
301          .. draw2d::DrawIndices::default()
302        }],
303        draw_lineloop: false,
304        draw_crosshair: false
305      }
306    );
307    // create overlay viewport
308    let overlay = render::viewport::Builder::new (
309      glium::Rect { width, height, left: 0, bottom: 0 }
310    ).with_camera_3d (false).build();
311    assert!(self.viewports.insert (OVERLAY_VIEWPORT, overlay).is_none());
312  }
313
314  pub fn demo_handle_winit_window_event (&mut self,
315    event              : winit::event::WindowEvent,
316    running            : &mut bool,
317    mouse_position     : &mut (f64, f64),
318    mouse_button_event : &mut Option <winit::event::ElementState>
319  ) {
320    use std::f32::consts::PI;
321    use winit::{event, keyboard};
322    use num::Zero;
323    log::debug!("demo handle winit window event: {event:?}");
324    // winit deprecated the modifiers field of keyboard input so we have to
325    // track modifier changes
326    lazy_static!{
327      static ref MODIFIERS : Arc <RwLock <keyboard::ModifiersState>> =
328        Arc::new (RwLock::new (keyboard::ModifiersState::empty()));
329    }
330    match event {
331      // window resized event
332      event::WindowEvent::Resized (physical_size) => {
333        log::debug!("window event: {event:?}");
334        self.window_resized (physical_size);
335      }
336      // window closed event
337      event::WindowEvent::CloseRequested => { *running = false; }
338      // TODO: for some reason a destroyed event is received on startup even
339      // though window has not been destroyed (winit 27.5)
340      event::WindowEvent::Destroyed => { /* *running = false; */ }
341      // modifiers changed event
342      event::WindowEvent::ModifiersChanged (modifiers) =>
343        *MODIFIERS.write().unwrap() = modifiers.state(),
344      // keyboard input pressed event
345      event::WindowEvent::KeyboardInput {
346        event: event::KeyEvent {
347          state: event::ElementState::Pressed,
348          physical_key, logical_key, location, ..
349        },
350        ..
351      } => match (logical_key, location) {
352        ( keyboard::Key::Named (keyboard::NamedKey::Control),
353          keyboard::KeyLocation::Left
354        ) => self.camera3d_move_local_xy (0.0, 0.0, -1.0),
355        _ => match physical_key {
356          keyboard::PhysicalKey::Code (key_code) => match key_code {
357            // quit
358            keyboard::KeyCode::KeyQ | keyboard::KeyCode::Escape =>
359              *running = false,
360            // choose a frame function 0-9
361            keyboard::KeyCode::Digit1 => self.frame_fun =
362              render::frame_fun_default::<render::resource::Default>,
363            // TODO: more frame functions
364            // TODO: cycle between frame functions
365            //keyboard::KeyCode::Tab => { }
366            keyboard::KeyCode::KeyW =>
367              self.camera3d_move_local_xy (0.0, 1.0, 0.0),
368            keyboard::KeyCode::KeyS =>
369              self.camera3d_move_local_xy (0.0, -1.0, 0.0),
370            keyboard::KeyCode::KeyD =>
371              self.camera3d_move_local_xy (1.0, 0.0, 0.0),
372            keyboard::KeyCode::KeyA =>
373              self.camera3d_move_local_xy (-1.0, 0.0, 0.0),
374            keyboard::KeyCode::Space | keyboard::KeyCode::KeyR =>
375              self.camera3d_move_local_xy (0.0, 0.0, 1.0),
376            keyboard::KeyCode::KeyF =>
377              self.camera3d_move_local_xy (0.0, 0.0, -1.0),
378            keyboard::KeyCode::KeyJ | keyboard::KeyCode::ArrowDown => {
379              let modifiers = MODIFIERS.read().unwrap();
380              if modifiers.alt_key() && modifiers.shift_key() {
381                self.camera2d_move_local (0.0, -1.0);
382              } else {
383                self.camera3d_rotate (
384                  math::Rad::zero(),
385                  math::Rad (-PI / 12.0),
386                  math::Rad::zero());
387              }
388            }
389            keyboard::KeyCode::KeyK | keyboard::KeyCode::ArrowUp => {
390              let modifiers = MODIFIERS.read().unwrap();
391              if modifiers.alt_key() && modifiers.shift_key() {
392                self.camera2d_move_local (0.0, 1.0);
393              } else {
394                self.camera3d_rotate (
395                  math::Rad::zero(),
396                  math::Rad (PI / 12.0),
397                  math::Rad::zero());
398              }
399            }
400            keyboard::KeyCode::KeyH | keyboard::KeyCode::ArrowLeft => {
401              let modifiers = MODIFIERS.read().unwrap();
402              if modifiers.alt_key() && modifiers.shift_key() {
403                self.camera2d_move_local (-1.0, 0.0);
404              } else {
405                self.camera3d_rotate (
406                  math::Rad (PI / 12.0),
407                  math::Rad::zero(),
408                  math::Rad::zero());
409              }
410            }
411            keyboard::KeyCode::KeyL | keyboard::KeyCode::ArrowRight => {
412              let modifiers = MODIFIERS.read().unwrap();
413              if modifiers.alt_key() && modifiers.shift_key() {
414                self.camera2d_move_local (1.0, 0.0);
415              } else {
416                self.camera3d_rotate (
417                  math::Rad (-PI / 12.0),
418                  math::Rad::zero(),
419                  math::Rad::zero());
420              }
421            }
422            keyboard::KeyCode::KeyI => {
423              let modifiers = MODIFIERS.read().unwrap();
424              if modifiers.alt_key() && modifiers.shift_key() {
425                self.camera2d_zoom_shift (1.0);
426              } else if modifiers.alt_key() {
427                self.camera3d_orthographic_zoom_scale (1.1);
428              } else {
429                self.camera3d_perspective_fovy_scale (1.0 / 1.1);
430              }
431            }
432            keyboard::KeyCode::KeyO => {
433              let modifiers = MODIFIERS.read().unwrap();
434              if modifiers.alt_key() && modifiers.shift_key() {
435                self.camera2d_zoom_shift (-1.0);
436              } else if modifiers.alt_key() {
437                self.camera3d_orthographic_zoom_scale (1.0 / 1.1);
438              } else {
439                self.camera3d_perspective_fovy_scale (1.1);
440              }
441            }
442            // toggle 4-viewport mode
443            keyboard::KeyCode::KeyX   => self.demo_toggle_quad_viewports(),
444            // end camera controls
445            keyboard::KeyCode::F10 | keyboard::KeyCode::PrintScreen =>
446              self.screenshot(),
447            _ => {}
448          }
449          keyboard::PhysicalKey::Unidentified (_) => {}
450        }
451      } // end keyboard input
452      event::WindowEvent::MouseInput { button, state, .. } =>
453        if button == event::MouseButton::Left {
454          *mouse_button_event = Some (state)
455        }
456      event::WindowEvent::CursorMoved { position, ..  } => {
457        if self.resource.draw2d.draw_pointer.is_some() {
458          let (_, height) = self.glium_display.get_framebuffer_dimensions();
459          let x = position.x as f32;
460          let y = height as f32 - position.y as f32;
461          self.resource
462            .set_pointer_position (&self.glium_display, [x, y].into());
463        }
464        *mouse_position = (position.x, position.y);
465      }
466      _ => {}
467    }
468  }
469
470  /// Switch between single (perspective) and quad viewport modes (perspective
471  /// + three ortho viewports).
472  ///
473  /// Generally should be called from a render context that was initialized
474  /// with `demo_init`.
475  ///
476  /// # Panics
477  ///
478  /// If there is a single viewport (other than the `OVERLAY_VIEWPORT`) it must
479  /// be `MAIN_VIEWPORT`.
480  ///
481  /// If there are multiple viewports, they must be exactly the first four
482  /// viewports only (other than the `OVERLAY_VIEWPORT`).
483  // TODO: doctests, make safe to call from any state?
484  fn demo_toggle_quad_viewports (&mut self) {
485    if self.viewports.len() <= 2 {
486      // switch to four viewports from single viewport
487      // main viewport becomes "lower right"
488      let (
489        left_width, right_width, upper_height, lower_height, position,
490        position_2d, zoom_2d
491      ) = {
492        let lower_right   = &mut self.viewports[MAIN_VIEWPORT];
493        let window_width  = lower_right.rect().width;
494        let window_height = lower_right.rect().height;
495        let left_width    = left_width   (window_width);
496        let right_width   = right_width  (window_width);
497        let upper_height  = upper_height (window_height);
498        let lower_height  = lower_height (window_height);
499        lower_right.set_rect (glium::Rect {
500          width:  right_width,
501          height: lower_height,
502          left:   left_width,
503          bottom: 0
504        });
505        ( left_width, right_width, upper_height, lower_height,
506          lower_right.camera3d().unwrap().position(),
507          lower_right.camera2d().unwrap().position(),
508          lower_right.camera2d().unwrap().zoom()
509        )
510      };
511
512      let upper_left  = render::viewport::Builder::new (glium::Rect {
513        width:  left_width,
514        height: upper_height,
515        left:   0,
516        bottom: lower_height
517      }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
518        .orthographic_3d (1.0)
519        .with_pose_3d (
520          // looking down positive Y axis
521          math::Pose3 { position, angles: math::Angles3::default() })
522        .build();
523
524      let upper_right = render::viewport::Builder::new (glium::Rect {
525        width:  right_width,
526        height: upper_height,
527        left:   left_width,
528        bottom: lower_height,
529      }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
530        .orthographic_3d (1.0)
531        .with_pose_3d (
532          math::Pose3 {
533            position,
534            // looking down positive X axis
535            angles: math::Angles3::wrap (
536              math::Deg (-90.0).into(),
537              math::Rad (0.0),
538              math::Rad (0.0))
539          })
540        .build();
541
542      let lower_left  = render::viewport::Builder::new (glium::Rect {
543        width:  left_width,
544        height: lower_height,
545        left:   0,
546        bottom: 0,
547      }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
548        .orthographic_3d (1.0)
549        .with_pose_3d (
550          math::Pose3 {
551            position,
552            // looking down negative Z axis
553            angles: math::Angles3::wrap (
554              math::Rad (0.0),
555              math::Deg (-90.0).into(),
556              math::Rad (0.0))
557          })
558        .build();
559
560      assert!(self.viewports.insert (UPPER_LEFT_VIEWPORT, upper_left)
561        .is_none());
562      assert!(self.viewports.insert (UPPER_RIGHT_VIEWPORT, upper_right)
563        .is_none());
564      assert!(self.viewports.insert (LOWER_LEFT_VIEWPORT, lower_left)
565        .is_none());
566    } else {
567      // switch to single viewport from four viewports
568      assert!(self.viewports.len() == 4 || self.viewports.len() == 5);
569      assert!(self.viewports.remove (UPPER_LEFT_VIEWPORT) .is_some());
570      assert!(self.viewports.remove (UPPER_RIGHT_VIEWPORT).is_some());
571      assert!(self.viewports.remove (LOWER_LEFT_VIEWPORT) .is_some());
572      let main_viewport   = &mut self.viewports[MAIN_VIEWPORT];
573      let (width, height) = self.window.inner_size().into();
574      main_viewport.set_rect (glium::Rect {
575        width,
576        height,
577        left:   0,
578        bottom: 0
579      });
580    }
581    self.update_viewport_line_loops();
582  } // end fn toggle_quad_viewports
583}