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
12pub 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 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 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 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 self.resource.draw2d.draw_pointer =
97 Some (DefaultTexturePointerId::Hand as PointerTextureIndexRepr);
98 self.window.set_cursor_visible (false);
100
101 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 let aabb_triangles_vertex = [vertex::Vert3dOrientationScaleColor {
113 color: color::rgba_u8_to_rgba_f32 (color::DEBUG_GREY),
114 .. aabb_lines_vertex[0]
115 }];
116 let grid_vertex_data = Default::debug_grid_vertices();
118 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 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 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 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 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 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, 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 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 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 .. draw2d::ViewportResources::default()
306 }
307 );
308 let overlay = render::viewport::Builder::new (glium::Rect {
310 width: width,
311 height: height,
312 left: 0,
313 bottom: 0
314 }).with_camera_3d (false)
315 .build();
316 assert!(self.viewports.insert (OVERLAY_VIEWPORT, overlay).is_none());
317 }
318
319 pub fn demo_handle_winit_window_event (&mut self,
320 event : winit::event::WindowEvent,
321 running : &mut bool,
322 mouse_position : &mut (f64, f64),
323 mouse_button_event : &mut Option <winit::event::ElementState>
324 ) {
325 use std::f32::consts::PI;
326 use winit::{event, keyboard};
327 use num::Zero;
328 log::debug!("demo handle winit window event: {:?}", event);
329 lazy_static!{
332 static ref MODIFIERS : Arc <RwLock <keyboard::ModifiersState>> =
333 Arc::new (RwLock::new (keyboard::ModifiersState::empty()));
334 }
335 match event {
336 event::WindowEvent::Resized (physical_size) => {
338 log::debug!("window event: {:?}", event);
339 self.window_resized (physical_size);
340 }
341 event::WindowEvent::CloseRequested => { *running = false; }
343 event::WindowEvent::Destroyed => { }
346 event::WindowEvent::ModifiersChanged (modifiers) =>
348 *MODIFIERS.write().unwrap() = modifiers.state(),
349 event::WindowEvent::KeyboardInput {
351 event: event::KeyEvent {
352 state: event::ElementState::Pressed,
353 physical_key, logical_key, location, ..
354 },
355 ..
356 } => match (logical_key, location) {
357 ( keyboard::Key::Named (keyboard::NamedKey::Control),
358 keyboard::KeyLocation::Left
359 ) => self.camera3d_move_local_xy (0.0, 0.0, -1.0),
360 _ => match physical_key {
361 keyboard::PhysicalKey::Code (key_code) => match key_code {
362 keyboard::KeyCode::KeyQ | keyboard::KeyCode::Escape =>
364 *running = false,
365 keyboard::KeyCode::Digit1 => self.frame_fun =
367 render::frame_fun_default::<render::resource::Default>,
368 keyboard::KeyCode::KeyW =>
372 self.camera3d_move_local_xy (0.0, 1.0, 0.0),
373 keyboard::KeyCode::KeyS =>
374 self.camera3d_move_local_xy (0.0, -1.0, 0.0),
375 keyboard::KeyCode::KeyD =>
376 self.camera3d_move_local_xy (1.0, 0.0, 0.0),
377 keyboard::KeyCode::KeyA =>
378 self.camera3d_move_local_xy (-1.0, 0.0, 0.0),
379 keyboard::KeyCode::Space | keyboard::KeyCode::KeyR =>
380 self.camera3d_move_local_xy (0.0, 0.0, 1.0),
381 keyboard::KeyCode::KeyF =>
382 self.camera3d_move_local_xy (0.0, 0.0, -1.0),
383 keyboard::KeyCode::KeyJ | keyboard::KeyCode::ArrowDown => {
384 let modifiers = MODIFIERS.read().unwrap();
385 if modifiers.alt_key() && modifiers.shift_key() {
386 self.camera2d_move_local (0.0, -1.0);
387 } else {
388 self.camera3d_rotate (
389 math::Rad::zero(),
390 math::Rad (-PI / 12.0),
391 math::Rad::zero());
392 }
393 }
394 keyboard::KeyCode::KeyK | keyboard::KeyCode::ArrowUp => {
395 let modifiers = MODIFIERS.read().unwrap();
396 if modifiers.alt_key() && modifiers.shift_key() {
397 self.camera2d_move_local (0.0, 1.0);
398 } else {
399 self.camera3d_rotate (
400 math::Rad::zero(),
401 math::Rad (PI / 12.0),
402 math::Rad::zero());
403 }
404 }
405 keyboard::KeyCode::KeyH | keyboard::KeyCode::ArrowLeft => {
406 let modifiers = MODIFIERS.read().unwrap();
407 if modifiers.alt_key() && modifiers.shift_key() {
408 self.camera2d_move_local (-1.0, 0.0);
409 } else {
410 self.camera3d_rotate (
411 math::Rad (PI / 12.0),
412 math::Rad::zero(),
413 math::Rad::zero());
414 }
415 }
416 keyboard::KeyCode::KeyL | keyboard::KeyCode::ArrowRight => {
417 let modifiers = MODIFIERS.read().unwrap();
418 if modifiers.alt_key() && modifiers.shift_key() {
419 self.camera2d_move_local (1.0, 0.0);
420 } else {
421 self.camera3d_rotate (
422 math::Rad (-PI / 12.0),
423 math::Rad::zero(),
424 math::Rad::zero());
425 }
426 }
427 keyboard::KeyCode::KeyI => {
428 let modifiers = MODIFIERS.read().unwrap();
429 if modifiers.alt_key() && modifiers.shift_key() {
430 self.camera2d_zoom_shift (1.0);
431 } else if modifiers.alt_key() {
432 self.camera3d_orthographic_zoom_scale (1.1);
433 } else {
434 self.camera3d_perspective_fovy_scale (1.0 / 1.1);
435 }
436 }
437 keyboard::KeyCode::KeyO => {
438 let modifiers = MODIFIERS.read().unwrap();
439 if modifiers.alt_key() && modifiers.shift_key() {
440 self.camera2d_zoom_shift (-1.0);
441 } else if modifiers.alt_key() {
442 self.camera3d_orthographic_zoom_scale (1.0 / 1.1);
443 } else {
444 self.camera3d_perspective_fovy_scale (1.1);
445 }
446 }
447 keyboard::KeyCode::KeyX => self.demo_toggle_quad_viewports(),
449 keyboard::KeyCode::F10 | keyboard::KeyCode::PrintScreen =>
451 self.screenshot(),
452 _ => {}
453 }
454 keyboard::PhysicalKey::Unidentified (_) => {}
455 }
456 } event::WindowEvent::MouseInput { button, state, .. } => match button {
458 event::MouseButton::Left => *mouse_button_event = Some (state),
459 _ => {}
460 }
461 event::WindowEvent::CursorMoved { position, .. } => {
462 if self.resource.draw2d.draw_pointer.is_some() {
463 let (_, height) = self.glium_display.get_framebuffer_dimensions();
464 let x = position.x as f32;
465 let y = height as f32 - position.y as f32;
466 self.resource
467 .set_pointer_position (&self.glium_display, [x, y].into());
468 }
469 *mouse_position = (position.x, position.y);
470 }
471 _ => {}
472 }
473 }
474
475 fn demo_toggle_quad_viewports (&mut self) {
490 if self.viewports.len() <= 2 {
491 let (
494 left_width, right_width, upper_height, lower_height, position,
495 position_2d, zoom_2d
496 ) = {
497 let lower_right = &mut self.viewports[MAIN_VIEWPORT];
498 let window_width = lower_right.rect().width;
499 let window_height = lower_right.rect().height;
500 let left_width = left_width (window_width);
501 let right_width = right_width (window_width);
502 let upper_height = upper_height (window_height);
503 let lower_height = lower_height (window_height);
504 lower_right.set_rect (glium::Rect {
505 width: right_width,
506 height: lower_height,
507 left: left_width,
508 bottom: 0
509 });
510 ( left_width, right_width, upper_height, lower_height,
511 lower_right.camera3d().unwrap().position(),
512 lower_right.camera2d().unwrap().position(),
513 lower_right.camera2d().unwrap().zoom()
514 )
515 };
516
517 let upper_left = render::viewport::Builder::new (glium::Rect {
518 width: left_width,
519 height: upper_height,
520 left: 0,
521 bottom: lower_height
522 }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
523 .orthographic_3d (1.0)
524 .with_pose_3d (
525 math::Pose3 { position, angles: math::Angles3::default() })
527 .build();
528
529 let upper_right = render::viewport::Builder::new (glium::Rect {
530 width: right_width,
531 height: upper_height,
532 left: left_width,
533 bottom: lower_height,
534 }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
535 .orthographic_3d (1.0)
536 .with_pose_3d (
537 math::Pose3 {
538 position,
539 angles: math::Angles3::wrap (
541 math::Deg (-90.0).into(),
542 math::Rad (0.0),
543 math::Rad (0.0))
544 })
545 .build();
546
547 let lower_left = render::viewport::Builder::new (glium::Rect {
548 width: left_width,
549 height: lower_height,
550 left: 0,
551 bottom: 0,
552 }).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
553 .orthographic_3d (1.0)
554 .with_pose_3d (
555 math::Pose3 {
556 position,
557 angles: math::Angles3::wrap (
559 math::Rad (0.0),
560 math::Deg (-90.0).into(),
561 math::Rad (0.0))
562 })
563 .build();
564
565 assert!(self.viewports.insert (UPPER_LEFT_VIEWPORT, upper_left)
566 .is_none());
567 assert!(self.viewports.insert (UPPER_RIGHT_VIEWPORT, upper_right)
568 .is_none());
569 assert!(self.viewports.insert (LOWER_LEFT_VIEWPORT, lower_left)
570 .is_none());
571 } else {
572 assert!(self.viewports.len() == 4 || self.viewports.len() == 5);
574 assert!(self.viewports.remove (UPPER_LEFT_VIEWPORT) .is_some());
575 assert!(self.viewports.remove (UPPER_RIGHT_VIEWPORT).is_some());
576 assert!(self.viewports.remove (LOWER_LEFT_VIEWPORT) .is_some());
577 let main_viewport = &mut self.viewports[MAIN_VIEWPORT];
578 let (width, height) = self.window.inner_size().into();
579 main_viewport.set_rect (glium::Rect {
580 width,
581 height,
582 left: 0,
583 bottom: 0
584 });
585 }
586 self.update_viewport_line_loops();
587 } }