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)]
149#[derive(Default)]
150pub enum DefaultTilesetId {
151 #[default]
153 EasciiAcorn128,
154 EasciiAcorn256,
155 EasciiAcornInverse128,
157 EasciiAcornInverse256
158}
159
160impl Default {
165 pub fn debug_grid_vertices() -> [vertex::Vert3dOrientationScaleColor; 3] {
168 use std::f32::consts::FRAC_PI_2;
169 const HALF_GRID_DIMS : f32 = 0.5 * draw3d::MESH_GRID_DIMS as f32;
170 [
171 vertex::Vert3dOrientationScaleColor {
173 position: [HALF_GRID_DIMS, 0.0, HALF_GRID_DIMS],
174 orientation: (*math::Rotation3::from_angle_y (math::Rad (FRAC_PI_2)))
175 .into_col_arrays(),
176 scale: [1.0, 1.0, 1.0],
177 color: color::rgba_u8_to_rgba_f32 ([255, 0, 0, 255])
178 },
179 vertex::Vert3dOrientationScaleColor {
181 position: [0.0, HALF_GRID_DIMS, HALF_GRID_DIMS],
182 orientation: (*math::Rotation3::from_angle_x (math::Rad (FRAC_PI_2)))
183 .into_col_arrays(),
184 scale: [1.0, 1.0, 1.0],
185 color: color::rgba_u8_to_rgba_f32 ([0, 255, 0, 255])
186 },
187 vertex::Vert3dOrientationScaleColor {
189 position: [0.0, 0.0, 0.0],
190 orientation: math::Matrix3::identity().into_col_arrays(),
191 scale: [1.0, 1.0, 1.0],
192 color: color::rgba_u8_to_rgba_f32 ([0, 0, 255, 255])
193 }
194 ]
195 }
196 #[inline]
198 pub fn tile_dimensions (&self, tileset_id : DefaultTilesetId) -> [u32; 2] {
199 let (width, height) = match tileset_id {
200 DefaultTilesetId::EasciiAcorn128 =>
201 self.tileset_128x128_texture.dimensions(),
202 DefaultTilesetId::EasciiAcorn256 =>
203 self.tileset_256x256_texture.dimensions(),
204 _ => unimplemented!()
205 };
206 debug_assert_eq!(width % 16, 0);
207 debug_assert_eq!(height % 16, 0);
208 [width / 16, height / 16]
209 }
210 #[inline]
211 pub fn shader_programs (&self) -> &VecMap <glium::Program> {
212 &self.shader_programs
213 }
214 #[inline]
215 pub fn set_pointer_position (&mut self,
216 display : &glium::Display <glutin::surface::WindowSurface>,
217 position : math::Point2 <f32>
218 ) {
219 let offset = self.draw2d.draw_pointer.map_or (
220 math::Vector2::zero(),
221 |texture_index| self.textures_pointer.get (texture_index as usize)
222 .unwrap().1.numcast().unwrap()
223 );
224 self.draw2d.set_pointer_vertex (display, position + offset);
225 }
226 #[inline]
227 pub fn set_textures_16x16 (&mut self,
228 textures_16x16 : glium::texture::Texture2dArray
229 ) {
230 assert_eq!(textures_16x16.width(), 16);
231 assert_eq!(textures_16x16.height(), 16);
232 self.textures_16x16 = textures_16x16;
233 }
234 #[inline]
235 pub fn set_textures_64x64 (&mut self,
236 textures_64x64 : glium::texture::Texture2dArray
237 ) {
238 assert_eq!(textures_64x64.width(), 64);
239 assert_eq!(textures_64x64.height(), 64);
240 self.textures_64x64 = textures_64x64;
241 }
242}
243
244impl render::Resource for Default {
245 fn new (glium_display : &glium::Display <glutin::surface::WindowSurface>)
246 -> Self
247 {
248 let shader_programs = shader::build_programs (glium_display).unwrap();
250 let default_textures_16x16 =
252 texture::texture2darray_with_mipmaps_from_bytes (
253 glium_display,
254 &DEFAULT_TEXTURES_16X16_BYTES.values().map (Deref::deref)
255 .collect::<Vec <&[u8]>>(),
256 image::ImageFormat::Png,
257 glium::texture::MipmapsOption::NoMipmap
258 ).unwrap();
259 let textures_16x16 =
260 glium::texture::Texture2dArray::empty (glium_display, 16, 16, 0).unwrap();
261 let textures_64x64 =
262 glium::texture::Texture2dArray::empty (glium_display, 64, 64, 0).unwrap();
263 let textures_anysize = VecMap::new();
264 let textures_pointer = DEFAULT_TEXTURES_POINTER_BYTES_OFFSETS.iter()
265 .map (|(i, (bytes, offset))|{
266 let texture = texture::texture2d_with_mipmaps_from_bytes (
267 glium_display, bytes, image::ImageFormat::Png,
268 glium::texture::MipmapsOption::NoMipmap
269 ).unwrap();
270 (i, (texture, math::Vector2::from (*offset)))
271 }).collect();
272 let tileset_128x128_texture = texture::texture2d_with_mipmaps_from_bytes (
273 glium_display,
274 texture::TILESET_EASCII_ACORN_8X8_PNG_FILE_BYTES,
276 image::ImageFormat::Png,
277 glium::texture::MipmapsOption::NoMipmap
278 ).unwrap();
279 let tileset_256x256_texture = texture::texture2d_with_mipmaps_from_bytes (
280 glium_display,
281 texture::TILESET_EASCII_ACORN_16X16_PNG_FILE_BYTES,
283 image::ImageFormat::Png,
284 glium::texture::MipmapsOption::NoMipmap
285 ).unwrap();
286 let draw2d = Draw2d::new (glium_display);
299 let draw3d = Draw3d::new (glium_display);
300
301 Default {
302 shader_programs,
303 default_textures_16x16,
304 textures_16x16,
305 textures_64x64,
306 textures_anysize,
307 textures_pointer,
308 tileset_128x128_texture,
309 tileset_256x256_texture,
310 draw2d,
311 draw3d
312 }
313 }
314
315 #[inline]
316 fn init (render : &mut Render <Self>) {
317 render.update_viewport_line_loops();
318 }
319
320 #[inline]
321 fn draw_2d (render : &Render <Self>, glium_frame : &mut glium::Frame) {
322 Draw2d::draw (render, glium_frame);
323 }
324
325 #[inline]
326 fn draw_3d (render : &Render <Self>, glium_frame : &mut glium::Frame) {
327 Draw3d::draw (render, glium_frame);
328 } #[inline]
331 fn reset (render : &mut Render <Self>) {
332 render.resource.draw2d = Draw2d::new (&render.glium_display);
333 render.resource.draw3d = Draw3d::new (&render.glium_display);
334 }
335} impl Render <Default> {
338
339 #[inline]
340 pub fn camera3d_position_set (&mut self, position : math::Point3 <f32>) {
341 for (_, viewport) in self.viewports.iter_mut() {
342 if viewport.camera3d().is_some() {
343 viewport.camera3d_set_position (position);
344 }
345 }
346 }
347
348 #[inline]
349 pub fn camera3d_orientation_set (&mut self,
350 orientation : math::Rotation3 <f32>
351 ) {
352 for (_, viewport) in self.viewports.iter_mut() {
353 if viewport.camera3d().is_some() {
354 viewport.camera3d_set_orientation (orientation);
355 }
356 }
357 }
358
359 #[inline]
360 pub fn camera3d_look_at (&mut self, target : math::Point3 <f32>) {
361 for (_, viewport) in self.viewports.iter_mut() {
362 if viewport.camera3d().is_some() {
363 viewport.camera3d_look_at (target);
364 }
365 }
366 }
367
368 pub fn camera3d_move_local_xy (&mut self, dx : f32, dy : f32, dz : f32) {
369 self.viewports[MAIN_VIEWPORT].camera3d_move_local_xy (dx, dy, dz);
370 let position = self.viewports[MAIN_VIEWPORT].camera3d().unwrap().position();
371 for (_, viewport) in self.viewports.iter_mut().skip (1) {
372 if viewport.camera3d().is_some() {
373 viewport.camera3d_set_position (position);
374 }
375 }
376 }
377
378 #[inline]
379 pub fn camera3d_rotate (&mut self,
380 dyaw : math::Rad <f32>, dpitch : math::Rad <f32>, droll : math::Rad <f32>
381 ) {
382 self.viewports[MAIN_VIEWPORT].camera3d_rotate (dyaw, dpitch, droll);
383 }
384
385 pub fn camera3d_orthographic_zoom_scale (&mut self, scale : f32) {
393 assert!(0.0 < scale);
394 for (_, viewport) in self.viewports.iter_mut().skip(1) {
395 if viewport.camera3d().is_some() {
396 debug_assert!(viewport.camera3d().unwrap().projection()
397 .is_orthographic());
398 viewport.camera3d_scale_fovy_or_zoom (scale);
399 }
400 }
401 }
402
403 pub fn camera3d_perspective_fovy_scale (&mut self, scale : f32) {
409 assert!(0.0 < scale);
410 debug_assert!(self.viewports[MAIN_VIEWPORT].camera3d().unwrap().projection()
411 .is_perspective());
412 self.viewports[MAIN_VIEWPORT].camera3d_scale_fovy_or_zoom (scale);
413 }
414
415 pub fn camera2d_zoom_set (&mut self, zoom : f32) {
416 assert!(0.0 < zoom);
417 for (_, viewport) in self.viewports.iter_mut() {
418 if viewport.camera2d().is_some() {
419 viewport.camera2d_set_zoom (zoom);
420 }
421 }
422 self.update_viewport_line_loops();
423 }
424
425 pub fn camera2d_zoom_shift (&mut self, shift : f32) {
427 let mut dirty = false;
428 for (_, viewport) in self.viewports.iter_mut() {
429 if viewport.camera2d().is_some() {
430 let zoom = viewport.camera2d().unwrap().zoom() + shift;
431 if 0.0 < zoom {
432 viewport.camera2d_set_zoom (zoom);
433 dirty = true;
434 }
435 }
436 }
437 if dirty {
438 self.update_viewport_line_loops();
439 }
440 }
441
442 pub fn camera2d_move_local (&mut self, dx : f32, dy : f32) {
443 self.viewports[MAIN_VIEWPORT].camera2d_move_local (dx, dy);
444 let position = self.viewports[MAIN_VIEWPORT].camera2d().unwrap().position();
445 for (_, viewport) in self.viewports.iter_mut().skip (1) {
446 if viewport.camera2d().is_some() {
447 viewport.camera2d_set_position (position);
448 }
449 }
450 }
451
452 pub fn camera2d_move_origin_to_bottom_left (&mut self) {
454 for (_, viewport) in self.viewports.iter_mut() {
455 if viewport.camera2d().is_some() {
456 viewport.camera2d_move_origin_to_bottom_left()
457 }
458 }
459 self.update_viewport_line_loops();
460 }
461
462 pub fn window_resized (&mut self,
466 physical_size : winit::dpi::PhysicalSize <u32>
467 ) {
468 let (width, height) = physical_size.into();
469 if self.viewports.len() < 4 {
470 self.viewports[MAIN_VIEWPORT].set_rect (
471 glium::Rect { left: 0, bottom: 0, width, height }
472 );
473 self.viewports.get_mut (OVERLAY_VIEWPORT).map (|viewport|
474 viewport.set_rect (glium::Rect { left: 0, bottom: 0, width, height }));
475 } else {
476 let left_width = left_width (width);
478 let right_width = right_width (width);
479 let upper_height = upper_height (height);
480 let lower_height = lower_height (height);
481 self.viewports[LOWER_RIGHT_VIEWPORT].set_rect (
482 glium::Rect {
483 width: right_width,
484 height: lower_height,
485 left: left_width,
486 bottom: 0
487 }
488 );
489 self.viewports[UPPER_LEFT_VIEWPORT].set_rect (
490 glium::Rect {
491 width: left_width,
492 height: upper_height,
493 left: 0,
494 bottom: lower_height
495 }
496 );
497 self.viewports[UPPER_RIGHT_VIEWPORT].set_rect (
498 glium::Rect {
499 width: right_width,
500 height: upper_height,
501 left: left_width,
502 bottom: lower_height
503 }
504 );
505 self.viewports[LOWER_LEFT_VIEWPORT].set_rect (
506 glium::Rect {
507 width: left_width,
508 height: lower_height,
509 left: 0,
510 bottom: 0
511 }
512 );
513 self.viewports.get_mut (OVERLAY_VIEWPORT).map (|viewport|
514 viewport.set_rect (glium::Rect { left: 0, bottom: 0, width, height }));
515 }
516 self.update_viewport_line_loops();
517 }
518
519 fn update_viewport_line_loops (&mut self) {
524 const VERTS_PER_VIEWPORT : usize = 4;
525 let num_vertices = VERTS_PER_VIEWPORT * self.viewports.len();
526 let vertices = {
527 let mut vertices = Vec::<vertex::Vert2d>::with_capacity (num_vertices);
528 for (_, viewport) in self.viewports.iter() {
529 if let Some (camera2d) = viewport.camera2d() {
530 let pixel_radius = 0.5 / camera2d.zoom();
532 let position = camera2d.position();
533 let ortho = camera2d.ortho();
534 let left = position.0.x + ortho.left;
535 let bottom = position.0.y + ortho.bottom;
536 let top = position.0.y + ortho.top;
537 let right = position.0.x + ortho.right;
538 vertices.append (&mut vec![
539 vertex::Vert2d {
540 position: [left + pixel_radius, bottom + pixel_radius] },
541 vertex::Vert2d {
542 position: [left + pixel_radius, top - pixel_radius] },
543 vertex::Vert2d {
544 position: [right - pixel_radius, top - pixel_radius] },
545 vertex::Vert2d {
546 position: [right - pixel_radius, bottom + pixel_radius] }
547 ]);
548 }
549 }
550 vertices
551 };
552 self.resource.draw2d
553 .line_loop_vertices_set (&self.glium_display, &vertices[..]);
554 }
555}
556
557
558#[inline]
567fn left_width (window_width : u32) -> u32 {
568 window_width / 2
569}
570#[inline]
571fn right_width (window_width : u32) -> u32 {
572 window_width / 2 + window_width % 2
573}
574#[inline]
575fn upper_height (window_height : u32) -> u32 {
576 window_height / 2
577}
578#[inline]
579fn lower_height (window_height : u32) -> u32 {
580 window_height / 2 + window_height % 2
581}