use std::sync::{Arc, RwLock};
use lazy_static::lazy_static;
use strum::IntoEnumIterator;
use winit;
use math_utils as math;
use math_utils::num_traits as num;
use crate::{tile, Render};
use super::*;
pub struct App {
pub render : Render <Default>,
pub mouse_position : (f64, f64),
pub mouse_button_event : Option <winit::event::ElementState>,
pub running : bool
}
impl App {
pub fn new (
glium_display : glium::Display <glutin::surface::WindowSurface>,
window : winit::window::Window
) -> Self {
let mut render = Render::<Default>::new (glium_display, window);
render.demo_init();
let mouse_position = (0.0, 0.0);
let mouse_button_event = None;
let running = true;
Self {
render, mouse_position, mouse_button_event, running
}
}
}
impl winit::application::ApplicationHandler for App {
fn resumed (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) { }
fn window_event (&mut self,
_event_loop : &winit::event_loop::ActiveEventLoop,
_window_id : winit::window::WindowId,
event : winit::event::WindowEvent
) {
self.render.demo_handle_winit_window_event (
event,
&mut self.running,
&mut self.mouse_position,
&mut self.mouse_button_event)
}
}
impl Render <Default> {
fn demo_init (&mut self) {
use draw3d::*;
println!(">>> Initializing gl-utils demo...");
println!(" Press 'Q' or 'Esc' to quit");
println!(" Horizontal movement: 'W', 'A', 'S', 'D'");
println!(" Vertical movement: 'Space', 'LCtrl' or 'R', 'F'");
println!(" Rotation: 'Up', 'Down', 'Left', 'Right' or 'H', 'J', 'K', 'L'");
println!(" Zoom in/out 3D (perspective): 'I', 'O'");
println!(" Zoom in/out 3D (orthographic): 'Alt+I', 'Alt+O'");
println!(" Zoom in/out 2D: 'Shift+Alt+I', 'Shift+Alt+O'");
println!(" Toggle split into 4 viewports with orthographic views: 'X'");
println!(" Screenshot: 'F10'");
self.resource.draw2d.draw_pointer =
Some (DefaultTexturePointerId::Hand as PointerTextureIndexRepr);
self.window.set_cursor_visible (false);
let aabb_lines_vertex = [vertex::Vert3dOrientationScaleColor {
position: [-0.5, 2.5, 1.0],
orientation: math::Matrix3::identity().into_col_arrays(),
scale: [0.5, 0.5, 1.0],
color: color::rgba_u8_to_rgba_f32 (color::DEBUG_PINK)
}];
let aabb_triangles_vertex = [vertex::Vert3dOrientationScaleColor {
color: color::rgba_u8_to_rgba_f32 (color::DEBUG_GREY),
.. aabb_lines_vertex[0]
}];
let grid_vertex_data = Default::debug_grid_vertices();
let hemisphere_vertex = [vertex::Vert3dOrientationScaleColor {
position: [2.5, 2.5, 0.0],
orientation: math::Matrix3::identity().into_col_arrays(),
scale: [0.5, 0.5, 0.5],
color: color::rgba_u8_to_rgba_f32 (color::DEBUG_VIOLET)
}];
let sphere_vertex = [vertex::Vert3dOrientationScaleColor {
position: [1.5, 2.5, 0.5],
orientation: math::Matrix3::identity().into_col_arrays(),
scale: [0.5, 0.5, 0.5],
color: color::rgba_u8_to_rgba_f32 (color::DEBUG_RED)
}];
let capsule_vertex = [vertex::Vert3dOrientationScaleColor {
position: [0.5, 2.5, 1.0],
orientation: math::Matrix3::identity().into_col_arrays(),
scale: [0.5, 0.5, 1.0],
color: color::rgba_u8_to_rgba_f32 (color::DEBUG_AZURE)
}];
let cylinder_vertex = [vertex::Vert3dOrientationScaleColor {
position: [3.5, 2.5, 1.0],
orientation: math::Matrix3::identity().into_col_arrays(),
scale: [0.5, 0.5, 1.0],
color: color::rgba_u8_to_rgba_f32 (color::DEBUG_LIGHT_BLUE)
}];
let aabb_lines = Some (&aabb_lines_vertex[..]);
let aabb_triangles = Some (&aabb_triangles_vertex[..]);
let aabb_lines_and_triangles = None;
let meshes = {
let mut v = VecMap::with_capacity (MeshId::COUNT);
assert!(v.insert (MeshId::Grid as usize, &grid_vertex_data[..])
.is_none());
assert!(v.insert (MeshId::Hemisphere as usize, &hemisphere_vertex[..])
.is_none());
assert!(v.insert (MeshId::Sphere as usize, &sphere_vertex[..])
.is_none());
assert!(v.insert (MeshId::Capsule as usize, &capsule_vertex[..])
.is_none());
assert!(v.insert (MeshId::Cylinder as usize, &cylinder_vertex[..])
.is_none());
v
};
let billboards = VecMap::new();
self.resource.draw3d.instance_vertices_set (
&self.glium_display,
draw3d::InstancesInit {
aabb_lines, aabb_triangles, aabb_lines_and_triangles, meshes, billboards
}
);
for mesh_id in MeshId::iter() {
let key = mesh_id as usize;
self.resource.draw3d.tile_billboard_create (
&self.glium_display, &self.resource.tileset_128x128_texture,
key, self.resource.draw3d.instanced_meshes()[key].instances_range.clone(),
format!("{:?}", mesh_id).as_str()
);
}
let aabb_billboard_key = MeshId::COUNT;
self.resource.draw3d.tile_billboard_create (
&self.glium_display, &self.resource.tileset_128x128_texture,
aabb_billboard_key, self.resource.draw3d.instanced_aabb_lines().clone(),
"Aabb"
);
let (width, height) = self.window.inner_size().into();
let [_tile_width, tile_height] =
self.resource.tile_dimensions (DefaultTilesetId::EasciiAcorn128);
let mut tile_2d_vertices = tile::vertices ("Viewport0", (0, 0));
let viewport0_tile_range = 0..tile_2d_vertices.len() as u32;
tile_2d_vertices.extend (tile::vertices ("Viewport1: +Y", (0, 0)));
let viewport1_tile_range = viewport0_tile_range.end..tile_2d_vertices.len()
as u32;
tile_2d_vertices.extend (tile::vertices ("Viewport2: +X", (0, 0)));
let viewport2_tile_range = viewport1_tile_range.end..tile_2d_vertices.len()
as u32;
tile_2d_vertices.extend (tile::vertices ("Viewport3: -Z", (0, 0)));
let viewport3_tile_range = viewport2_tile_range.end..tile_2d_vertices.len()
as u32;
let fps_row = ((height / tile_height) - 1) as i32;
tile_2d_vertices.extend (tile::vertices ("FPS: 0 ", (fps_row, 0)));
let viewport4_tile_range = viewport3_tile_range.end..tile_2d_vertices.len()
as u32;
self.resource.draw2d.tile_2d_vertices = glium::VertexBuffer::dynamic (
&self.glium_display, &tile_2d_vertices[..]
).unwrap();
let tile_color_2d_vertices = {
let tiles = tile::vertices ("abc123", (2, 0));
let colors = vec![
( [1.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
( [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
( [0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
( [0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 0.0, 1.0] ),
( [0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 1.0, 1.0] ),
( [0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 1.0, 1.0] )
];
tiles.into_iter().zip (colors).map (
|(vertex::Vert2dTile { tile, row, column }, (fg, bg))|
vertex::Vert2dTileColor { tile, row, column, fg, bg }
).collect::<Vec <_>>()
};
let viewport0_tile_color_range = 0..tile_color_2d_vertices.len() as u32;
self.resource.draw2d.tile_color_2d_vertices = glium::VertexBuffer::dynamic (
&self.glium_display, &tile_color_2d_vertices[..]
).unwrap();
let draw_tiles = draw2d::Tiles {
vertex_range: 0..0, origin: (2, 2).into(),
tileset_id: DefaultTilesetId::EasciiAcorn128
};
let draw_tiles_color = draw2d::Tiles {
vertex_range: 0..0, origin: draw2d::TilesOrigin::World,
tileset_id: DefaultTilesetId::EasciiAcorn256
};
self.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT,
draw2d::ViewportResources {
draw_indices: vec![draw2d::DrawIndices {
rectangle: None,
draw_tiles: Some (draw2d::Tiles {
vertex_range: viewport0_tile_range,
.. draw_tiles.clone()
}),
draw_color_tiles: Some (draw2d::Tiles {
vertex_range: viewport0_tile_color_range,
.. draw_tiles_color
})
}],
.. draw2d::ViewportResources::default()
}
);
self.resource.draw2d.viewport_resources_set (UPPER_LEFT_VIEWPORT,
draw2d::ViewportResources {
draw_indices: vec![draw2d::DrawIndices {
draw_tiles: Some (draw2d::Tiles {
vertex_range: viewport1_tile_range,
.. draw_tiles.clone()
}),
.. draw2d::DrawIndices::default()
}],
.. draw2d::ViewportResources::default()
}
);
self.resource.draw2d.viewport_resources_set (UPPER_RIGHT_VIEWPORT,
draw2d::ViewportResources {
draw_indices: vec![draw2d::DrawIndices {
draw_tiles: Some (draw2d::Tiles {
vertex_range: viewport2_tile_range,
.. draw_tiles.clone()
}),
.. draw2d::DrawIndices::default()
}],
.. draw2d::ViewportResources::default()
}
);
self.resource.draw2d.viewport_resources_set (LOWER_LEFT_VIEWPORT,
draw2d::ViewportResources {
draw_indices: vec![draw2d::DrawIndices {
draw_tiles: Some (draw2d::Tiles {
vertex_range: viewport3_tile_range,
.. draw_tiles.clone()
}),
.. draw2d::DrawIndices::default()
}],
.. draw2d::ViewportResources::default()
}
);
self.resource.draw2d.viewport_resources_set (OVERLAY_VIEWPORT,
draw2d::ViewportResources {
draw_indices: vec![draw2d::DrawIndices {
draw_tiles: Some (draw2d::Tiles {
vertex_range: viewport4_tile_range,
.. draw_tiles.clone()
}),
.. draw2d::DrawIndices::default()
}],
draw_lineloop: false,
draw_crosshair: false,
.. draw2d::ViewportResources::default()
}
);
let overlay = render::viewport::Builder::new (glium::Rect {
width: width,
height: height,
left: 0,
bottom: 0
}).with_camera_3d (false)
.build();
assert!(self.viewports.insert (OVERLAY_VIEWPORT, overlay).is_none());
}
pub fn demo_handle_winit_window_event (&mut self,
event : winit::event::WindowEvent,
running : &mut bool,
mouse_position : &mut (f64, f64),
mouse_button_event : &mut Option <winit::event::ElementState>
) {
use std::f32::consts::PI;
use winit::{event, keyboard};
use num::Zero;
log::debug!("demo handle winit window event: {:?}", event);
lazy_static!{
static ref MODIFIERS : Arc <RwLock <keyboard::ModifiersState>> =
Arc::new (RwLock::new (keyboard::ModifiersState::empty()));
}
match event {
event::WindowEvent::Resized (physical_size) => {
log::debug!("window event: {:?}", event);
self.window_resized (physical_size);
}
event::WindowEvent::CloseRequested => { *running = false; }
event::WindowEvent::Destroyed => { }
event::WindowEvent::ModifiersChanged (modifiers) =>
*MODIFIERS.write().unwrap() = modifiers.state(),
event::WindowEvent::KeyboardInput {
event: event::KeyEvent {
state: event::ElementState::Pressed,
physical_key, logical_key, location, ..
},
..
} => match (logical_key, location) {
( keyboard::Key::Named (keyboard::NamedKey::Control),
keyboard::KeyLocation::Left
) => self.camera3d_move_local_xy (0.0, 0.0, -1.0),
_ => match physical_key {
keyboard::PhysicalKey::Code (key_code) => match key_code {
keyboard::KeyCode::KeyQ | keyboard::KeyCode::Escape =>
*running = false,
keyboard::KeyCode::Digit1 => self.frame_fun =
render::frame_fun_default::<render::resource::Default>,
keyboard::KeyCode::KeyW =>
self.camera3d_move_local_xy (0.0, 1.0, 0.0),
keyboard::KeyCode::KeyS =>
self.camera3d_move_local_xy (0.0, -1.0, 0.0),
keyboard::KeyCode::KeyD =>
self.camera3d_move_local_xy (1.0, 0.0, 0.0),
keyboard::KeyCode::KeyA =>
self.camera3d_move_local_xy (-1.0, 0.0, 0.0),
keyboard::KeyCode::Space | keyboard::KeyCode::KeyR =>
self.camera3d_move_local_xy (0.0, 0.0, 1.0),
keyboard::KeyCode::KeyF =>
self.camera3d_move_local_xy (0.0, 0.0, -1.0),
keyboard::KeyCode::KeyJ | keyboard::KeyCode::ArrowDown => {
let modifiers = MODIFIERS.read().unwrap();
if modifiers.alt_key() && modifiers.shift_key() {
self.camera2d_move_local (0.0, -1.0);
} else {
self.camera3d_rotate (
math::Rad::zero(),
math::Rad (-PI / 12.0),
math::Rad::zero());
}
}
keyboard::KeyCode::KeyK | keyboard::KeyCode::ArrowUp => {
let modifiers = MODIFIERS.read().unwrap();
if modifiers.alt_key() && modifiers.shift_key() {
self.camera2d_move_local (0.0, 1.0);
} else {
self.camera3d_rotate (
math::Rad::zero(),
math::Rad (PI / 12.0),
math::Rad::zero());
}
}
keyboard::KeyCode::KeyH | keyboard::KeyCode::ArrowLeft => {
let modifiers = MODIFIERS.read().unwrap();
if modifiers.alt_key() && modifiers.shift_key() {
self.camera2d_move_local (-1.0, 0.0);
} else {
self.camera3d_rotate (
math::Rad (PI / 12.0),
math::Rad::zero(),
math::Rad::zero());
}
}
keyboard::KeyCode::KeyL | keyboard::KeyCode::ArrowRight => {
let modifiers = MODIFIERS.read().unwrap();
if modifiers.alt_key() && modifiers.shift_key() {
self.camera2d_move_local (1.0, 0.0);
} else {
self.camera3d_rotate (
math::Rad (-PI / 12.0),
math::Rad::zero(),
math::Rad::zero());
}
}
keyboard::KeyCode::KeyI => {
let modifiers = MODIFIERS.read().unwrap();
if modifiers.alt_key() && modifiers.shift_key() {
self.camera2d_zoom_shift (1.0);
} else if modifiers.alt_key() {
self.camera3d_orthographic_zoom_scale (1.1);
} else {
self.camera3d_perspective_fovy_scale (1.0 / 1.1);
}
}
keyboard::KeyCode::KeyO => {
let modifiers = MODIFIERS.read().unwrap();
if modifiers.alt_key() && modifiers.shift_key() {
self.camera2d_zoom_shift (-1.0);
} else if modifiers.alt_key() {
self.camera3d_orthographic_zoom_scale (1.0 / 1.1);
} else {
self.camera3d_perspective_fovy_scale (1.1);
}
}
keyboard::KeyCode::KeyX => self.demo_toggle_quad_viewports(),
keyboard::KeyCode::F10 | keyboard::KeyCode::PrintScreen =>
self.screenshot(),
_ => {}
}
keyboard::PhysicalKey::Unidentified (_) => {}
}
} event::WindowEvent::MouseInput { button, state, .. } => match button {
event::MouseButton::Left => *mouse_button_event = Some (state),
_ => {}
}
event::WindowEvent::CursorMoved { position, .. } => {
if self.resource.draw2d.draw_pointer.is_some() {
let (_, height) = self.glium_display.get_framebuffer_dimensions();
let x = position.x as f32;
let y = height as f32 - position.y as f32;
self.resource
.set_pointer_position (&self.glium_display, [x, y].into());
}
*mouse_position = (position.x, position.y);
}
_ => {}
}
}
fn demo_toggle_quad_viewports (&mut self) {
if self.viewports.len() <= 2 {
let (
left_width, right_width, upper_height, lower_height, position,
position_2d, zoom_2d
) = {
let lower_right = &mut self.viewports[MAIN_VIEWPORT];
let window_width = lower_right.rect().width;
let window_height = lower_right.rect().height;
let left_width = left_width (window_width);
let right_width = right_width (window_width);
let upper_height = upper_height (window_height);
let lower_height = lower_height (window_height);
lower_right.set_rect (glium::Rect {
width: right_width,
height: lower_height,
left: left_width,
bottom: 0
});
( left_width, right_width, upper_height, lower_height,
lower_right.camera3d().unwrap().position(),
lower_right.camera2d().unwrap().position(),
lower_right.camera2d().unwrap().zoom()
)
};
let upper_left = render::viewport::Builder::new (glium::Rect {
width: left_width,
height: upper_height,
left: 0,
bottom: lower_height
}).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
.orthographic_3d (1.0)
.with_pose_3d (
math::Pose3 { position, angles: math::Angles3::default() })
.build();
let upper_right = render::viewport::Builder::new (glium::Rect {
width: right_width,
height: upper_height,
left: left_width,
bottom: lower_height,
}).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
.orthographic_3d (1.0)
.with_pose_3d (
math::Pose3 {
position,
angles: math::Angles3::wrap (
math::Deg (-90.0).into(),
math::Rad (0.0),
math::Rad (0.0))
})
.build();
let lower_left = render::viewport::Builder::new (glium::Rect {
width: left_width,
height: lower_height,
left: 0,
bottom: 0,
}).with_zoom_2d (zoom_2d).with_position_2d (position_2d)
.orthographic_3d (1.0)
.with_pose_3d (
math::Pose3 {
position,
angles: math::Angles3::wrap (
math::Rad (0.0),
math::Deg (-90.0).into(),
math::Rad (0.0))
})
.build();
assert!(self.viewports.insert (UPPER_LEFT_VIEWPORT, upper_left)
.is_none());
assert!(self.viewports.insert (UPPER_RIGHT_VIEWPORT, upper_right)
.is_none());
assert!(self.viewports.insert (LOWER_LEFT_VIEWPORT, lower_left)
.is_none());
} else {
assert!(self.viewports.len() == 4 || self.viewports.len() == 5);
assert!(self.viewports.remove (UPPER_LEFT_VIEWPORT) .is_some());
assert!(self.viewports.remove (UPPER_RIGHT_VIEWPORT).is_some());
assert!(self.viewports.remove (LOWER_LEFT_VIEWPORT) .is_some());
let main_viewport = &mut self.viewports[MAIN_VIEWPORT];
let (width, height) = self.window.inner_size().into();
main_viewport.set_rect (glium::Rect {
width,
height,
left: 0,
bottom: 0
});
}
self.update_viewport_line_loops();
} }