1use std::iter::FromIterator;
5use std::ops::Deref;
6
7use glium;
8use image;
9use lazy_static::lazy_static;
10use strum::{EnumCount, EnumIter, FromRepr};
11use vec_map::VecMap;
12use winit;
13
14use math_utils as math;
15
16use crate::{color, render, shader, texture, vertex, Render};
17
18pub mod draw2d;
19pub mod draw3d;
20#[cfg(feature="demo")]
21#[cfg_attr(docsrs, doc(cfg(feature="demo")))]
22pub mod demo;
23
24pub use self::draw2d::Draw2d;
25pub use self::draw3d::Draw3d;
26
27pub const MAIN_VIEWPORT : usize = 0;
32#[allow(dead_code)]
33pub const LOWER_RIGHT_VIEWPORT : usize = MAIN_VIEWPORT;
34pub const UPPER_LEFT_VIEWPORT : usize = 1;
35pub const UPPER_RIGHT_VIEWPORT : usize = 2;
36pub const LOWER_LEFT_VIEWPORT : usize = 3;
37pub const OVERLAY_VIEWPORT : usize = 4;
38
39lazy_static!{
44 static ref DEFAULT_TEXTURES_16X16_BYTES : VecMap <&'static [u8]> =
45 VecMap::from_iter ([
46 ( DefaultTexture16Id::Crosshair as usize,
47 texture::CROSSHAIR_PNG_FILE_BYTES.as_slice()
48 ), (
49 DefaultTexture16Id::CrosshairInverse as usize,
50 texture::CROSSHAIR_INVERSE_PNG_FILE_BYTES.as_slice()
51 )
52 ]);
53
54 static ref DEFAULT_TEXTURES_POINTER_BYTES_OFFSETS :
55 VecMap <(&'static [u8], [i16; 2])> = VecMap::from_iter ([
56 ( DefaultTexturePointerId::Hand as usize,
57 ( texture::POINTER_HAND_PNG_FILE_BYTES_OFFSET.0.as_slice(),
58 texture::POINTER_HAND_PNG_FILE_BYTES_OFFSET.1
59 )
60 )
61 ]);
62}
63
64pub type PointerTextureIndexRepr = u16;
69
70pub trait Resource {
83 fn new (glium_display : &glium::Display <glutin::surface::WindowSurface>)
84 -> Self;
85 fn init (_render : &mut Render <Self>) where Self : Sized
86 { }
87 fn reset (render : &mut Render <Self>) where Self : Sized {
89 render.resource = Self::new (&render.glium_display)
90 }
91 fn draw_2d (_render : &Render <Self>, _glium_frame : &mut glium::Frame) where
92 Self : Sized { }
93 fn draw_3d (_render : &Render <Self>, _glium_frame : &mut glium::Frame) where
94 Self : Sized { }
95}
96
97pub struct Default {
104 pub draw2d : Draw2d,
105 pub draw3d : Draw3d,
106 pub textures_anysize : VecMap <glium::texture::Texture2d>,
108 pub textures_pointer :
110 VecMap <(glium::texture::Texture2d, math::Vector2 <i16>)>,
111 pub tileset_128x128_texture : glium::texture::Texture2d,
113 pub tileset_256x256_texture : glium::texture::Texture2d,
115 shader_programs : VecMap <glium::Program>,
117 default_textures_16x16 : glium::texture::Texture2dArray,
119 textures_16x16 : glium::texture::Texture2dArray,
121 textures_64x64 : glium::texture::Texture2dArray
123}
124
125#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, EnumCount, EnumIter,
130 FromRepr)]
131#[repr(u16)]
132pub enum DefaultTexturePointerId {
133 Hand
134}
135
136#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, EnumCount, EnumIter,
137 FromRepr)]
138#[repr(u16)]
139pub enum DefaultTexture16Id {
140 Crosshair,
142 CrosshairInverse
144}
145
146#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, EnumCount, EnumIter,
147 FromRepr)]
148#[repr(u16)]
149pub enum DefaultTilesetId {
150 EasciiAcorn128,
152 EasciiAcorn256,
153 EasciiAcornInverse128,
155 EasciiAcornInverse256
156}
157
158impl Default {
163 pub fn debug_grid_vertices() -> [vertex::Vert3dOrientationScaleColor; 3] {
166 use std::f32::consts::FRAC_PI_2;
167 const HALF_GRID_DIMS : f32 = 0.5 * draw3d::MESH_GRID_DIMS as f32;
168 [
169 vertex::Vert3dOrientationScaleColor {
171 position: [HALF_GRID_DIMS, 0.0, HALF_GRID_DIMS],
172 orientation: (*math::Rotation3::from_angle_y (math::Rad (FRAC_PI_2)))
173 .into_col_arrays(),
174 scale: [1.0, 1.0, 1.0],
175 color: color::rgba_u8_to_rgba_f32 ([255, 0, 0, 255])
176 },
177 vertex::Vert3dOrientationScaleColor {
179 position: [0.0, HALF_GRID_DIMS, HALF_GRID_DIMS],
180 orientation: (*math::Rotation3::from_angle_x (math::Rad (FRAC_PI_2)))
181 .into_col_arrays(),
182 scale: [1.0, 1.0, 1.0],
183 color: color::rgba_u8_to_rgba_f32 ([0, 255, 0, 255])
184 },
185 vertex::Vert3dOrientationScaleColor {
187 position: [0.0, 0.0, 0.0],
188 orientation: math::Matrix3::identity().into_col_arrays(),
189 scale: [1.0, 1.0, 1.0],
190 color: color::rgba_u8_to_rgba_f32 ([0, 0, 255, 255])
191 }
192 ]
193 }
194 #[inline]
196 pub fn tile_dimensions (&self, tileset_id : DefaultTilesetId) -> [u32; 2] {
197 let (width, height) = match tileset_id {
198 DefaultTilesetId::EasciiAcorn128 =>
199 self.tileset_128x128_texture.dimensions(),
200 DefaultTilesetId::EasciiAcorn256 =>
201 self.tileset_256x256_texture.dimensions(),
202 _ => unimplemented!()
203 };
204 debug_assert_eq!(width % 16, 0);
205 debug_assert_eq!(height % 16, 0);
206 [width / 16, height / 16]
207 }
208 #[inline]
209 pub fn shader_programs (&self) -> &VecMap <glium::Program> {
210 &self.shader_programs
211 }
212 #[inline]
213 pub fn set_pointer_position (&mut self,
214 display : &glium::Display <glutin::surface::WindowSurface>,
215 position : math::Point2 <f32>
216 ) {
217 let offset = self.draw2d.draw_pointer.clone().map_or (
218 math::Vector2::zero(),
219 |texture_index| self.textures_pointer.get (texture_index as usize)
220 .unwrap().1.numcast().unwrap()
221 );
222 self.draw2d.set_pointer_vertex (display, position + offset);
223 }
224 #[inline]
225 pub fn set_textures_16x16 (&mut self,
226 textures_16x16 : glium::texture::Texture2dArray
227 ) {
228 assert_eq!(textures_16x16.width(), 16);
229 assert_eq!(textures_16x16.height(), 16);
230 self.textures_16x16 = textures_16x16;
231 }
232 #[inline]
233 pub fn set_textures_64x64 (&mut self,
234 textures_64x64 : glium::texture::Texture2dArray
235 ) {
236 assert_eq!(textures_64x64.width(), 64);
237 assert_eq!(textures_64x64.height(), 64);
238 self.textures_64x64 = textures_64x64;
239 }
240}
241
242impl render::Resource for Default {
243 fn new (glium_display : &glium::Display <glutin::surface::WindowSurface>)
244 -> Self
245 {
246 let shader_programs = shader::build_programs (glium_display).unwrap();
248 let default_textures_16x16 =
250 texture::texture2darray_with_mipmaps_from_bytes (
251 glium_display,
252 &DEFAULT_TEXTURES_16X16_BYTES.values().map (Deref::deref)
253 .collect::<Vec <&[u8]>>(),
254 image::ImageFormat::Png,
255 glium::texture::MipmapsOption::NoMipmap
256 ).unwrap();
257 let textures_16x16 =
258 glium::texture::Texture2dArray::empty (glium_display, 16, 16, 0).unwrap();
259 let textures_64x64 =
260 glium::texture::Texture2dArray::empty (glium_display, 64, 64, 0).unwrap();
261 let textures_anysize = VecMap::new();
262 let textures_pointer = DEFAULT_TEXTURES_POINTER_BYTES_OFFSETS.iter()
263 .map (|(i, (bytes, offset))|{
264 let texture = texture::texture2d_with_mipmaps_from_bytes (
265 glium_display, bytes, image::ImageFormat::Png,
266 glium::texture::MipmapsOption::NoMipmap
267 ).unwrap();
268 (i, (texture, math::Vector2::from (*offset)))
269 }).collect();
270 let tileset_128x128_texture = texture::texture2d_with_mipmaps_from_bytes (
271 glium_display,
272 texture::TILESET_EASCII_ACORN_8X8_PNG_FILE_BYTES,
274 image::ImageFormat::Png,
275 glium::texture::MipmapsOption::NoMipmap
276 ).unwrap();
277 let tileset_256x256_texture = texture::texture2d_with_mipmaps_from_bytes (
278 glium_display,
279 texture::TILESET_EASCII_ACORN_16X16_PNG_FILE_BYTES,
281 image::ImageFormat::Png,
282 glium::texture::MipmapsOption::NoMipmap
283 ).unwrap();
284 let draw2d = Draw2d::new (glium_display);
297 let draw3d = Draw3d::new (glium_display);
298
299 Default {
300 shader_programs,
301 default_textures_16x16,
302 textures_16x16,
303 textures_64x64,
304 textures_anysize,
305 textures_pointer,
306 tileset_128x128_texture,
307 tileset_256x256_texture,
308 draw2d,
309 draw3d
310 }
311 }
312
313 #[inline]
314 fn init (render : &mut Render <Self>) {
315 render.update_viewport_line_loops();
316 }
317
318 #[inline]
319 fn draw_2d (render : &Render <Self>, glium_frame : &mut glium::Frame) {
320 Draw2d::draw (render, glium_frame);
321 }
322
323 #[inline]
324 fn draw_3d (render : &Render <Self>, glium_frame : &mut glium::Frame) {
325 Draw3d::draw (render, glium_frame);
326 } #[inline]
329 fn reset (render : &mut Render <Self>) {
330 render.resource.draw2d = Draw2d::new (&render.glium_display);
331 render.resource.draw3d = Draw3d::new (&render.glium_display);
332 }
333} impl Render <Default> {
336
337 #[inline]
338 pub fn camera3d_position_set (&mut self, position : math::Point3 <f32>) {
339 for (_, viewport) in self.viewports.iter_mut() {
340 if viewport.camera3d().is_some() {
341 viewport.camera3d_set_position (position);
342 }
343 }
344 }
345
346 #[inline]
347 pub fn camera3d_orientation_set (&mut self,
348 orientation : math::Rotation3 <f32>
349 ) {
350 for (_, viewport) in self.viewports.iter_mut() {
351 if viewport.camera3d().is_some() {
352 viewport.camera3d_set_orientation (orientation);
353 }
354 }
355 }
356
357 #[inline]
358 pub fn camera3d_look_at (&mut self, target : math::Point3 <f32>) {
359 for (_, viewport) in self.viewports.iter_mut() {
360 if viewport.camera3d().is_some() {
361 viewport.camera3d_look_at (target);
362 }
363 }
364 }
365
366 pub fn camera3d_move_local_xy (&mut self, dx : f32, dy : f32, dz : f32) {
367 self.viewports[MAIN_VIEWPORT].camera3d_move_local_xy (dx, dy, dz);
368 let position = self.viewports[MAIN_VIEWPORT].camera3d().unwrap().position();
369 for (_, viewport) in self.viewports.iter_mut().skip (1) {
370 if viewport.camera3d().is_some() {
371 viewport.camera3d_set_position (position);
372 }
373 }
374 }
375
376 #[inline]
377 pub fn camera3d_rotate (&mut self,
378 dyaw : math::Rad <f32>, dpitch : math::Rad <f32>, droll : math::Rad <f32>
379 ) {
380 self.viewports[MAIN_VIEWPORT].camera3d_rotate (dyaw, dpitch, droll);
381 }
382
383 pub fn camera3d_orthographic_zoom_scale (&mut self, scale : f32) {
391 assert!(0.0 < scale);
392 for (_, viewport) in self.viewports.iter_mut().skip(1) {
393 if viewport.camera3d().is_some() {
394 debug_assert!(viewport.camera3d().unwrap().projection()
395 .is_orthographic());
396 viewport.camera3d_scale_fovy_or_zoom (scale);
397 }
398 }
399 }
400
401 pub fn camera3d_perspective_fovy_scale (&mut self, scale : f32) {
407 assert!(0.0 < scale);
408 debug_assert!(self.viewports[MAIN_VIEWPORT].camera3d().unwrap().projection()
409 .is_perspective());
410 self.viewports[MAIN_VIEWPORT].camera3d_scale_fovy_or_zoom (scale);
411 }
412
413 pub fn camera2d_zoom_set (&mut self, zoom : f32) {
414 assert!(0.0 < zoom);
415 for (_, viewport) in self.viewports.iter_mut() {
416 if viewport.camera2d().is_some() {
417 viewport.camera2d_set_zoom (zoom);
418 }
419 }
420 self.update_viewport_line_loops();
421 }
422
423 pub fn camera2d_zoom_shift (&mut self, shift : f32) {
425 let mut dirty = false;
426 for (_, viewport) in self.viewports.iter_mut() {
427 if viewport.camera2d().is_some() {
428 let zoom = viewport.camera2d().unwrap().zoom() + shift;
429 if 0.0 < zoom {
430 viewport.camera2d_set_zoom (zoom);
431 dirty = true;
432 }
433 }
434 }
435 if dirty {
436 self.update_viewport_line_loops();
437 }
438 }
439
440 pub fn camera2d_move_local (&mut self, dx : f32, dy : f32) {
441 self.viewports[MAIN_VIEWPORT].camera2d_move_local (dx, dy);
442 let position = self.viewports[MAIN_VIEWPORT].camera2d().unwrap().position();
443 for (_, viewport) in self.viewports.iter_mut().skip (1) {
444 if viewport.camera2d().is_some() {
445 viewport.camera2d_set_position (position);
446 }
447 }
448 }
449
450 pub fn camera2d_move_origin_to_bottom_left (&mut self) {
452 for (_, viewport) in self.viewports.iter_mut() {
453 if viewport.camera2d().is_some() {
454 viewport.camera2d_move_origin_to_bottom_left()
455 }
456 }
457 self.update_viewport_line_loops();
458 }
459
460 pub fn window_resized (&mut self,
464 physical_size : winit::dpi::PhysicalSize <u32>
465 ) {
466 let (width, height) = physical_size.into();
467 if self.viewports.len() < 4 {
468 self.viewports[MAIN_VIEWPORT].set_rect (
469 glium::Rect { left: 0, bottom: 0, width, height }
470 );
471 self.viewports.get_mut (OVERLAY_VIEWPORT).map (|viewport|
472 viewport.set_rect (glium::Rect { left: 0, bottom: 0, width, height }));
473 } else {
474 let left_width = left_width (width);
476 let right_width = right_width (width);
477 let upper_height = upper_height (height);
478 let lower_height = lower_height (height);
479 self.viewports[LOWER_RIGHT_VIEWPORT].set_rect (
480 glium::Rect {
481 width: right_width,
482 height: lower_height,
483 left: left_width,
484 bottom: 0
485 }
486 );
487 self.viewports[UPPER_LEFT_VIEWPORT].set_rect (
488 glium::Rect {
489 width: left_width,
490 height: upper_height,
491 left: 0,
492 bottom: lower_height
493 }
494 );
495 self.viewports[UPPER_RIGHT_VIEWPORT].set_rect (
496 glium::Rect {
497 width: right_width,
498 height: upper_height,
499 left: left_width,
500 bottom: lower_height
501 }
502 );
503 self.viewports[LOWER_LEFT_VIEWPORT].set_rect (
504 glium::Rect {
505 width: left_width,
506 height: lower_height,
507 left: 0,
508 bottom: 0
509 }
510 );
511 self.viewports.get_mut (OVERLAY_VIEWPORT).map (|viewport|
512 viewport.set_rect (glium::Rect { left: 0, bottom: 0, width, height }));
513 }
514 self.update_viewport_line_loops();
515 }
516
517 fn update_viewport_line_loops (&mut self) {
522 const VERTS_PER_VIEWPORT : usize = 4;
523 let num_vertices = VERTS_PER_VIEWPORT * self.viewports.len();
524 let vertices = {
525 let mut vertices = Vec::<vertex::Vert2d>::with_capacity (num_vertices);
526 for (_, viewport) in self.viewports.iter() {
527 if let Some (camera2d) = viewport.camera2d() {
528 let pixel_radius = 0.5 / camera2d.zoom();
530 let position = camera2d.position();
531 let ortho = camera2d.ortho();
532 let left = position.0.x + ortho.left;
533 let bottom = position.0.y + ortho.bottom;
534 let top = position.0.y + ortho.top;
535 let right = position.0.x + ortho.right;
536 vertices.append (&mut vec![
537 vertex::Vert2d {
538 position: [left + pixel_radius, bottom + pixel_radius] },
539 vertex::Vert2d {
540 position: [left + pixel_radius, top - pixel_radius] },
541 vertex::Vert2d {
542 position: [right - pixel_radius, top - pixel_radius] },
543 vertex::Vert2d {
544 position: [right - pixel_radius, bottom + pixel_radius] }
545 ]);
546 }
547 }
548 vertices
549 };
550 self.resource.draw2d
551 .line_loop_vertices_set (&self.glium_display, &vertices[..]);
552 }
553}
554
555impl std::default::Default for DefaultTilesetId {
556 fn default() -> Self {
557 DefaultTilesetId::EasciiAcorn128
558 }
559}
560
561#[inline]
570fn left_width (window_width : u32) -> u32 {
571 window_width / 2
572}
573#[inline]
574fn right_width (window_width : u32) -> u32 {
575 window_width / 2 + window_width % 2
576}
577#[inline]
578fn upper_height (window_height : u32) -> u32 {
579 window_height / 2
580}
581#[inline]
582fn lower_height (window_height : u32) -> u32 {
583 window_height / 2 + window_height % 2
584}