1use std::sync::{Arc, LazyLock, RwLock};
2use strum::IntoEnumIterator;
3use winit;
4
5use math_utils as math;
6use math_utils::num;
7
8use crate::{render, tile, Render};
9use super::*;
10
11static MODIFIERS : LazyLock <Arc <RwLock <winit::keyboard::ModifiersState>>> =
14 LazyLock::new (|| Arc::new (RwLock::new (winit::keyboard::ModifiersState::empty())));
15
16pub struct App {
18 pub render : Render <Default>,
19 pub mouse_position : (f64, f64),
20 pub mouse_button_event : Option <winit::event::ElementState>,
21 pub running : bool
22}
23
24impl App {
25 pub fn new (
26 glium_display : glium::Display <glutin::surface::WindowSurface>,
27 window : winit::window::Window
28 ) -> Self {
29 let mut render = Render::<Default>::new (glium_display, window);
30 render.demo_init();
31 let mouse_position = (0.0, 0.0);
32 let mouse_button_event = None;
33 let running = true;
34 Self { render, mouse_position, mouse_button_event, running }
35 }
36}
37
38impl winit::application::ApplicationHandler for App {
39 fn resumed (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) { }
41
42 fn window_event (&mut self,
43 _event_loop : &winit::event_loop::ActiveEventLoop,
44 _window_id : winit::window::WindowId,
45 event : winit::event::WindowEvent
46 ) {
47 self.render.demo_handle_winit_window_event (
56 event,
57 &mut self.running,
58 &mut self.mouse_position,
59 &mut self.mouse_button_event)
60 }
61}
62
63impl Render <Default> {
64 pub fn modifiers() -> winit::keyboard::ModifiersState {
66 *MODIFIERS.read().unwrap()
67 }
68 fn demo_init (&mut self) {
88 use color::{Normalize, WithAlphaOpaque};
89 use draw3d::*;
90
91 println!(">>> Initializing gl-utils demo...");
92 println!(" Press 'Q' or 'Esc' to quit");
93 println!(" Horizontal movement: 'W', 'A', 'S', 'D'");
94 println!(" Vertical movement: 'Space', 'LCtrl' or 'R', 'F'");
95 println!(" Rotation: 'Up', 'Down', 'Left', 'Right' or 'H', 'J', 'K', 'L'");
96 println!(" Zoom in/out 3D (perspective): 'I', 'O'");
97 println!(" Zoom in/out 3D (orthographic): 'Alt+I', 'Alt+O'");
98 println!(" Zoom in/out 2D: 'Shift+Alt+I', 'Shift+Alt+O'");
99 println!(" Toggle split into 4 viewports with orthographic views: 'X'");
100 println!(" Screenshot: 'F10'");
101
102 self.resource.draw2d.draw_pointer =
104 Some (DefaultTexturePointerId::Hand as PointerTextureIndexRepr);
105 self.window.set_cursor_visible (false);
107
108 self.resource.draw3d.light_directional =
110 math::Unit3::normalize ([1.0, 1.0, -1.0].into());
111
112 let cube_vertex = [vertex::Vert3dOrientationScaleColor {
117 position: [-1.5, 2.5, 1.0],
118 orientation: math::Matrix3::identity().into_col_arrays(),
119 scale: [std::f32::consts::SQRT_3 / 6.0; 3],
120 color: color::CYAN.normalize().rgba().into()
121 }];
122 let aabb_lines_vertex = [vertex::Vert3dOrientationScaleColor {
124 position: [-0.5, 2.5, 1.0],
125 orientation: math::Matrix3::identity().into_col_arrays(),
126 scale: [0.5, 0.5, 1.0],
127 color: color::DEBUG_PINK.normalize().rgba().into()
128 }];
129 let aabb_triangles_vertex = [vertex::Vert3dOrientationScaleColor {
131 color: color::DEBUG_GREY.normalize().rgba().into(),
132 .. aabb_lines_vertex[0]
133 }];
134 let spheres_raycast_vertices = [
136 vertex::Vert3dOrientationScaleColor {
137 position: [-1.5, 2.5, 3.0],
138 orientation: math::Matrix3::identity().into_col_arrays(),
139 scale: [0.5, 0.5, 0.5],
140 color:
141 color::hue_luminance_custom (0.0, 44.0).normalize().rgba().into()
142 },
143 vertex::Vert3dOrientationScaleColor {
144 position: [0.0, 2.5, 3.0],
145 orientation: math::Matrix3::identity().into_col_arrays(),
146 scale: [0.5, 0.5, 0.5],
147 color:
148 color::hue_luminance_custom (120.0, 44.0).normalize().rgba().into()
149 },
150 vertex::Vert3dOrientationScaleColor {
151 position: [1.5, 2.5, 3.0],
152 orientation: math::Matrix3::identity().into_col_arrays(),
153 scale: [0.5, 0.5, 0.5],
154 color:
155 color::hue_luminance_custom (240.0, 44.0).normalize().rgba().into()
156 }
157 ];
158 let grid_vertex_data = Default::debug_grid_vertices();
160 let hemisphere_vertex = [vertex::Vert3dOrientationScaleColor {
162 position: [2.5, 2.5, 0.0],
163 orientation: math::Matrix3::identity().into_col_arrays(),
164 scale: [0.5, 0.5, 0.5],
165 color: color::DEBUG_VIOLET.normalize().rgba().into()
166 }];
167 let sphere_vertex = [vertex::Vert3dOrientationScaleColor {
170 position: [1.5, 2.5, 0.5],
171 orientation: math::Matrix3::identity().into_col_arrays(),
172 scale: [0.5, 0.5, 0.5],
173 color: color::DEBUG_RED.normalize().rgba().into()
174 }];
175 let capsule_vertex = [vertex::Vert3dOrientationScaleColor {
177 position: [0.5, 2.5, 1.0],
178 orientation: math::Matrix3::identity().into_col_arrays(),
179 scale: [0.5, 0.5, 1.0],
180 color: color::DEBUG_AZURE.normalize().rgba().into()
181 }];
182 let cylinder_vertex = [vertex::Vert3dOrientationScaleColor {
184 position: [3.5, 2.5, 1.0],
185 orientation: math::Matrix3::identity().into_col_arrays(),
186 scale: [0.5, 0.5, 1.0],
187 color: color::DEBUG_LIGHT_BLUE.normalize().rgba().into()
188 }];
189 let aabb_lines = Some (&aabb_lines_vertex[..]);
190 let aabb_triangles = Some (&aabb_triangles_vertex[..]);
191 let aabb_lines_and_triangles = None;
192 let spheres_raycast = Some (&spheres_raycast_vertices[..]);
193 let meshes = {
194 let mut v = VecMap::with_capacity (MeshId::COUNT);
195 assert!(v.insert (MeshId::Grid as usize, &grid_vertex_data[..]).is_none());
196 assert!(v.insert (MeshId::Hemisphere as usize, &hemisphere_vertex[..]).is_none());
197 assert!(v.insert (MeshId::Sphere as usize, &sphere_vertex[..]).is_none());
198 assert!(v.insert (MeshId::Capsule as usize, &capsule_vertex[..]).is_none());
199 assert!(v.insert (MeshId::Cube as usize, &cube_vertex[..]).is_none());
200 assert!(v.insert (MeshId::Cylinder as usize, &cylinder_vertex[..]).is_none());
201 v
202 };
203 let billboards = VecMap::new();
206 self.resource.draw3d.instance_vertices_set (
207 &self.glium_display,
208 InstancesInit {
209 aabb_lines, aabb_triangles, aabb_lines_and_triangles, spheres_raycast, meshes,
210 billboards
211 }
212 );
213 for mesh_id in MeshId::iter() {
215 let key = mesh_id as usize;
216 self.resource.draw3d.tile_billboard_create (
217 &self.glium_display, &self.resource.tileset_128x128_texture,
218 key, self.resource.draw3d.instanced_meshes()[key].instances_range.clone(),
219 format!("{mesh_id:?}").as_str()
220 );
221 }
222 let aabb_billboard_key = MeshId::COUNT;
223 self.resource.draw3d.tile_billboard_create (
224 &self.glium_display, &self.resource.tileset_128x128_texture,
225 aabb_billboard_key, self.resource.draw3d.instanced_aabb_lines().clone(),
227 "Aabb"
228 );
229
230 let (width, height) = self.window.inner_size().into();
231 let [_tile_width, tile_height] =
232 self.resource.tile_dimensions (DefaultTilesetId::EasciiAcorn128);
233 let mut tile_2d_vertices = tile::vertices ("Viewport0", (0, 0));
237 let viewport0_tile_range = 0..tile_2d_vertices.len() as u32;
238 tile_2d_vertices.extend (tile::vertices ("Viewport1: +Y", (0, 0)));
239 let viewport1_tile_range = viewport0_tile_range.end..tile_2d_vertices.len()
240 as u32;
241 tile_2d_vertices.extend (tile::vertices ("Viewport2: +X", (0, 0)));
242 let viewport2_tile_range = viewport1_tile_range.end..tile_2d_vertices.len()
243 as u32;
244 tile_2d_vertices.extend (tile::vertices ("Viewport3: -Z", (0, 0)));
245 let viewport3_tile_range = viewport2_tile_range.end..tile_2d_vertices.len()
246 as u32;
247 let fps_row = ((height / tile_height) - 1) as i32;
249 tile_2d_vertices.extend (tile::vertices ("FPS: 0 ", (fps_row, 0)));
250 let viewport4_tile_range = viewport3_tile_range.end..tile_2d_vertices.len()
251 as u32;
252 self.resource.draw2d.tile_2d_vertices = glium::VertexBuffer::dynamic (
253 &self.glium_display, &tile_2d_vertices[..]
254 ).unwrap();
255 let tile_color_2d_vertices = {
256 let tiles = tile::vertices ("abc123", (2, 0));
257 let colors = vec![
258 ( [1.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
259 ( [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
260 ( [0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
261 ( [0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 0.0, 1.0] ),
262 ( [0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 1.0, 1.0] ),
263 ( [0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 1.0] )
264 ];
265 tiles.into_iter().zip (colors).map (
266 |(vertex::Vert2dTile { tile, row, column }, (fg, bg))|
267 vertex::Vert2dTileColor { tile, row, column, fg, bg }
268 ).collect::<Vec <_>>()
269 };
270 let viewport0_tile_color_range = 0..tile_color_2d_vertices.len() as u32;
271 self.resource.draw2d.tile_color_2d_vertices = glium::VertexBuffer::dynamic (
272 &self.glium_display, &tile_color_2d_vertices[..]
273 ).unwrap();
274 let draw_tiles = draw2d::Tiles {
275 vertex_range: 0..0, origin: (2, 2).into(),
276 tileset_id: DefaultTilesetId::EasciiAcorn128
277 };
278 let draw_tiles_color = draw2d::Tiles {
279 vertex_range: 0..0, origin: draw2d::TilesOrigin::World,
280 tileset_id: DefaultTilesetId::EasciiAcorn256
281 };
282 self.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT,
283 draw2d::ViewportResources {
284 draw_indices: vec![draw2d::DrawIndices {
285 rectangle: None,
286 draw_tiles: Some (draw2d::Tiles {
287 vertex_range: viewport0_tile_range,
288 .. draw_tiles.clone()
289 }),
290 draw_color_tiles: Some (draw2d::Tiles {
291 vertex_range: viewport0_tile_color_range,
292 .. draw_tiles_color
293 })
294 }],
295 .. draw2d::ViewportResources::default()
296 }
297 );
298 self.resource.draw2d.viewport_resources_set (UPPER_LEFT_VIEWPORT,
299 draw2d::ViewportResources {
300 draw_indices: vec![draw2d::DrawIndices {
301 draw_tiles: Some (draw2d::Tiles {
302 vertex_range: viewport1_tile_range,
303 .. draw_tiles.clone()
304 }),
305 .. draw2d::DrawIndices::default()
306 }],
307 .. draw2d::ViewportResources::default()
308 }
309 );
310 self.resource.draw2d.viewport_resources_set (UPPER_RIGHT_VIEWPORT,
311 draw2d::ViewportResources {
312 draw_indices: vec![draw2d::DrawIndices {
313 draw_tiles: Some (draw2d::Tiles {
314 vertex_range: viewport2_tile_range,
315 .. draw_tiles.clone()
316 }),
317 .. draw2d::DrawIndices::default()
318 }],
319 .. draw2d::ViewportResources::default()
320 }
321 );
322 self.resource.draw2d.viewport_resources_set (LOWER_LEFT_VIEWPORT,
323 draw2d::ViewportResources {
324 draw_indices: vec![draw2d::DrawIndices {
325 draw_tiles: Some (draw2d::Tiles {
326 vertex_range: viewport3_tile_range,
327 .. draw_tiles.clone()
328 }),
329 .. draw2d::DrawIndices::default()
330 }],
331 .. draw2d::ViewportResources::default()
332 }
333 );
334 self.resource.draw2d.viewport_resources_set (OVERLAY_VIEWPORT,
335 draw2d::ViewportResources {
336 draw_indices: vec![draw2d::DrawIndices {
337 draw_tiles: Some (draw2d::Tiles {
338 vertex_range: viewport4_tile_range,
339 .. draw_tiles
340 }),
341 .. draw2d::DrawIndices::default()
342 }],
343 draw_lineloop: false,
344 draw_crosshair: false
345 }
346 );
347 let overlay = render::viewport::Builder::new (
349 glium::Rect { width, height, left: 0, bottom: 0 }
350 ).with_camera_3d (false).build();
351 assert!(self.viewports.insert (OVERLAY_VIEWPORT, overlay).is_none());
352 }
353
354 pub fn demo_handle_winit_window_event (&mut self,
355 event : winit::event::WindowEvent,
356 running : &mut bool,
357 mouse_position : &mut (f64, f64),
358 mouse_button_event : &mut Option <winit::event::ElementState>
359 ) {
360 use std::f32::consts::PI;
361 use winit::{event, keyboard};
362 use num::Zero;
363 log::debug!("demo handle winit window event: {event:?}");
364 match event {
365 event::WindowEvent::Resized (physical_size) => {
367 log::debug!("window event: {event:?}");
368 self.window_resized (physical_size);
369 }
370 event::WindowEvent::CloseRequested => { *running = false; }
372 #[expect(clippy::match_same_arms)]
375 event::WindowEvent::Destroyed => { }
376 event::WindowEvent::ModifiersChanged (modifiers) =>
378 *MODIFIERS.write().unwrap() = modifiers.state(),
379 event::WindowEvent::KeyboardInput {
381 event: event::KeyEvent {
382 state: event::ElementState::Pressed,
383 physical_key, logical_key, location, ..
384 },
385 ..
386 } => match (logical_key, location) {
387 ( keyboard::Key::Named (keyboard::NamedKey::Control),
388 keyboard::KeyLocation::Left
389 ) =>
390 if Self::modifiers().shift_key() {
391 self.camera3d_move_local_xy (0.0, 0.0, -0.5)
392 } else {
393 self.camera3d_move_local_xy (0.0, 0.0, -1.0)
394 }
395 _ => match physical_key {
396 keyboard::PhysicalKey::Code (key_code) => match key_code {
397 keyboard::KeyCode::KeyQ | keyboard::KeyCode::Escape =>
399 *running = false,
400 keyboard::KeyCode::Digit1 =>
402 self.frame_fun = render::frame_fun_default::<Default>,
403 keyboard::KeyCode::KeyW =>
407 if Self::modifiers().shift_key() {
408 self.camera3d_move_local_xy (0.0, 0.5, 0.0)
409 } else {
410 self.camera3d_move_local_xy (0.0, 1.0, 0.0)
411 }
412 keyboard::KeyCode::KeyS =>
413 if Self::modifiers().shift_key() {
414 self.camera3d_move_local_xy (0.0, -0.5, 0.0)
415 } else {
416 self.camera3d_move_local_xy (0.0, -1.0, 0.0)
417 }
418 keyboard::KeyCode::KeyD =>
419 if Self::modifiers().shift_key() {
420 self.camera3d_move_local_xy (0.5, 0.0, 0.0)
421 } else {
422 self.camera3d_move_local_xy (1.0, 0.0, 0.0)
423 }
424 keyboard::KeyCode::KeyA =>
425 if Self::modifiers().shift_key() {
426 self.camera3d_move_local_xy (-0.5, 0.0, 0.0)
427 } else {
428 self.camera3d_move_local_xy (-1.0, 0.0, 0.0)
429 }
430 keyboard::KeyCode::Space | keyboard::KeyCode::KeyR =>
431 if Self::modifiers().shift_key() {
432 self.camera3d_move_local_xy (0.0, 0.0, 0.5)
433 } else {
434 self.camera3d_move_local_xy (0.0, 0.0, 1.0)
435 }
436 keyboard::KeyCode::KeyF =>
437 if Self::modifiers().shift_key() {
438 self.camera3d_move_local_xy (0.0, 0.0, -0.5)
439 } else {
440 self.camera3d_move_local_xy (0.0, 0.0, -1.0)
441 }
442 keyboard::KeyCode::KeyJ | keyboard::KeyCode::ArrowDown => {
443 let modifiers = Self::modifiers();
444 if modifiers.alt_key() && modifiers.shift_key() {
445 self.camera2d_move_local (0.0, -1.0);
446 } else {
447 if Self::modifiers().shift_key() {
448 self.camera3d_rotate (
449 math::Rad::zero(),
450 math::Rad (-PI / 24.0),
451 math::Rad::zero());
452 } else {
453 self.camera3d_rotate (
454 math::Rad::zero(),
455 math::Rad (-PI / 12.0),
456 math::Rad::zero());
457 }
458 }
459 }
460 keyboard::KeyCode::KeyK | keyboard::KeyCode::ArrowUp => {
461 let modifiers = Self::modifiers();
462 if modifiers.alt_key() && modifiers.shift_key() {
463 self.camera2d_move_local (0.0, 1.0);
464 } else {
465 if Self::modifiers().shift_key() {
466 self.camera3d_rotate (
467 math::Rad::zero(),
468 math::Rad (PI / 24.0),
469 math::Rad::zero());
470 } else {
471 self.camera3d_rotate (
472 math::Rad::zero(),
473 math::Rad (PI / 12.0),
474 math::Rad::zero());
475 }
476 }
477 }
478 keyboard::KeyCode::KeyH | keyboard::KeyCode::ArrowLeft => {
479 let modifiers = Self::modifiers();
480 if modifiers.alt_key() && modifiers.shift_key() {
481 self.camera2d_move_local (-1.0, 0.0);
482 } else {
483 if Self::modifiers().shift_key() {
484 self.camera3d_rotate (
485 math::Rad (PI / 24.0),
486 math::Rad::zero(),
487 math::Rad::zero());
488 } else {
489 self.camera3d_rotate (
490 math::Rad (PI / 12.0),
491 math::Rad::zero(),
492 math::Rad::zero());
493 }
494 }
495 }
496 keyboard::KeyCode::KeyL | keyboard::KeyCode::ArrowRight => {
497 let modifiers = Self::modifiers();
498 if modifiers.alt_key() && modifiers.shift_key() {
499 self.camera2d_move_local (1.0, 0.0);
500 } else {
501 if Self::modifiers().shift_key() {
502 self.camera3d_rotate (
503 math::Rad (-PI / 24.0),
504 math::Rad::zero(),
505 math::Rad::zero());
506 } else {
507 self.camera3d_rotate (
508 math::Rad (-PI / 12.0),
509 math::Rad::zero(),
510 math::Rad::zero());
511 }
512 }
513 }
514 keyboard::KeyCode::KeyI => {
515 let modifiers = Self::modifiers();
516 if modifiers.alt_key() && modifiers.shift_key() {
517 self.camera2d_zoom_shift (1.0);
518 } else if modifiers.alt_key() {
519 self.camera3d_orthographic_zoom_scale (1.1);
520 } else {
521 self.camera3d_perspective_fovy_scale (1.0 / 1.1);
522 }
523 }
524 keyboard::KeyCode::KeyO => {
525 let modifiers = Self::modifiers();
526 if modifiers.alt_key() && modifiers.shift_key() {
527 self.camera2d_zoom_shift (-1.0);
528 } else if modifiers.alt_key() {
529 self.camera3d_orthographic_zoom_scale (1.0 / 1.1);
530 } else {
531 self.camera3d_perspective_fovy_scale (1.1);
532 }
533 }
534 keyboard::KeyCode::KeyX => self.demo_toggle_quad_viewports(),
536 keyboard::KeyCode::F10 | keyboard::KeyCode::PrintScreen =>
538 self.screenshot(),
539 _ => {}
540 }
541 keyboard::PhysicalKey::Unidentified (_) => {}
542 }
543 } event::WindowEvent::MouseInput { button: event::MouseButton::Left, state, .. } =>
545 *mouse_button_event = Some (state),
546 event::WindowEvent::CursorMoved { position, .. } => {
547 if self.resource.draw2d.draw_pointer.is_some() {
548 let (_, height) = self.glium_display.get_framebuffer_dimensions();
549 let x = position.x as f32;
550 let y = height as f32 - position.y as f32;
551 self.resource
552 .set_pointer_position (&self.glium_display, [x, y].into());
553 }
554 *mouse_position = (position.x, position.y);
555 }
556 _ => {}
557 }
558 }
559
560 fn demo_toggle_quad_viewports (&mut self) {
575 if self.viewports.len() <= 2 {
576 let (
579 left_width, right_width, upper_height, lower_height, position,
580 position_2d, zoom_2d
581 ) = {
582 let lower_right = &mut self.viewports[MAIN_VIEWPORT];
583 let window_width = lower_right.rect().width;
584 let window_height = lower_right.rect().height;
585 let left_width = left_width (window_width);
586 let right_width = right_width (window_width);
587 let upper_height = upper_height (window_height);
588 let lower_height = lower_height (window_height);
589 lower_right.set_rect (glium::Rect {
590 width: right_width,
591 height: lower_height,
592 left: left_width,
593 bottom: 0
594 });
595 ( left_width, right_width, upper_height, lower_height,
596 lower_right.camera3d().unwrap().position(),
597 lower_right.camera2d().unwrap().position(),
598 lower_right.camera2d().unwrap().zoom()
599 )
600 };
601
602 let upper_left = render::viewport::Builder::new (glium::Rect {
603 width: left_width,
604 height: upper_height,
605 left: 0,
606 bottom: lower_height
607 }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
608 .orthographic_3d (1.0)
609 .with_pose_3d (math::Pose3 { position, angles: math::Angles3::default() })
611 .build();
612
613 let upper_right = render::viewport::Builder::new (glium::Rect {
614 width: right_width,
615 height: upper_height,
616 left: left_width,
617 bottom: lower_height,
618 }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
619 .orthographic_3d (1.0)
620 .with_pose_3d (
621 math::Pose3 {
622 position,
623 angles: math::Angles3::wrap (
625 math::Deg (-90.0).into(),
626 math::Rad (0.0),
627 math::Rad (0.0))
628 })
629 .build();
630
631 let lower_left = render::viewport::Builder::new (glium::Rect {
632 width: left_width,
633 height: lower_height,
634 left: 0,
635 bottom: 0,
636 }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
637 .orthographic_3d (1.0)
638 .with_pose_3d (
639 math::Pose3 {
640 position,
641 angles: math::Angles3::wrap (
643 math::Rad (0.0),
644 math::Deg (-90.0).into(),
645 math::Rad (0.0))
646 })
647 .build();
648
649 assert!(self.viewports.insert (UPPER_LEFT_VIEWPORT, upper_left).is_none());
650 assert!(self.viewports.insert (UPPER_RIGHT_VIEWPORT, upper_right).is_none());
651 assert!(self.viewports.insert (LOWER_LEFT_VIEWPORT, lower_left).is_none());
652 } else {
653 assert!(self.viewports.len() == 4 || self.viewports.len() == 5);
655 assert!(self.viewports.remove (UPPER_LEFT_VIEWPORT) .is_some());
656 assert!(self.viewports.remove (UPPER_RIGHT_VIEWPORT).is_some());
657 assert!(self.viewports.remove (LOWER_LEFT_VIEWPORT) .is_some());
658 let main_viewport = &mut self.viewports[MAIN_VIEWPORT];
659 let (width, height) = self.window.inner_size().into();
660 main_viewport.set_rect (glium::Rect {
661 width,
662 height,
663 left: 0,
664 bottom: 0
665 });
666 }
667 self.update_viewport_line_loops();
668 } }