1use derive_more::{From, TryInto};
2use glium::{self, uniform};
3use strum::{EnumCount, EnumIter, FromRepr};
4use vec_map::VecMap;
5
6use crate::{camera2d, color, graphics, math, render, shader, vertex, Camera2d,
7 Render};
8use super::{DefaultTexture16Id, DefaultTilesetId, PointerTextureIndexRepr};
9
10pub struct Draw2d {
12 pub rectangle_2d_vertices : glium::VertexBuffer <vertex::Vert2dRectColor>,
16 pub line_loop_vertices : glium::VertexBuffer <vertex::Vert2d>,
19 pub sprite_16x16_vertices : glium::VertexBuffer <vertex::Vert2dLayer>,
22 pub sprite_64x64_vertices : glium::VertexBuffer <vertex::Vert2dLayer>,
24 pub rectangle_16x16_vertices :
27 glium::VertexBuffer <vertex::Vert2dRectUvLayer>,
28 pub rectangle_64x64_vertices :
31 glium::VertexBuffer <vertex::Vert2dRectUvLayer>,
32 pub rectangle_anysize_vertices :
33 VecMap <glium::VertexBuffer <vertex::Vert2dRectUv>>,
34 pointer_vertex : Option <glium::VertexBuffer <vertex::Vert2d>>,
36 pub draw_pointer : Option <PointerTextureIndexRepr>,
38 default_sprite_16x16_vertices : glium::VertexBuffer <vertex::Vert2dLayer>,
42 pub tile_2d_vertices : glium::VertexBuffer <vertex::Vert2dTile>,
46 pub tile_color_2d_vertices : glium::VertexBuffer <vertex::Vert2dTileColor>,
49 viewport_resources : VecMap <ViewportResources>,
52}
53
54#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, EnumCount, EnumIter,
55 FromRepr)]
56#[repr(u16)]
57pub enum DefaultSprite16Id {
58 Crosshair
62}
63
64#[derive(Clone, Debug)]
67pub struct ViewportResources {
68 pub draw_indices : Vec <DrawIndices>,
69 pub draw_crosshair : bool,
70 pub draw_lineloop : bool
71}
72
73#[derive(Clone, Debug, Default)]
75pub struct DrawIndices {
76 pub rectangle : Option <u32>,
78 pub draw_tiles : Option <Tiles>,
80 pub draw_color_tiles : Option <Tiles>
82}
83
84#[derive(Clone, Debug)]
86pub struct Tiles {
87 pub vertex_range : std::ops::Range <u32>,
90 pub origin : TilesOrigin,
93 pub tileset_id : DefaultTilesetId
94}
95
96#[derive(Clone, Debug, From, TryInto)]
98pub enum TilesOrigin {
99 Viewport {
104 offset_px_left : i16,
107 offset_px_top : i16
110 },
111 World
113}
114
115impl Draw2d {
116 pub fn new (glium_display : &glium::Display <glutin::surface::WindowSurface>)
117 -> Self
118 {
119 let rectangle_2d_vertices =
120 glium::VertexBuffer::dynamic (glium_display, &[ ]).unwrap();
121 let pointer_vertex = None;
122 let draw_pointer = None;
123 let default_sprite_16x16_vertices = glium::VertexBuffer::immutable (
124 glium_display,
125 &[vertex::Vert2dLayer {
127 position: [0.0, 0.0],
128 layer: DefaultTexture16Id::CrosshairInverse as u16
129 }]
130 ).unwrap();
131 let sprite_16x16_vertices =
132 glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
133 let sprite_64x64_vertices =
134 glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
135 let rectangle_16x16_vertices =
136 glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
137 let rectangle_64x64_vertices =
138 glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
139 let rectangle_anysize_vertices = VecMap::new();
140 let tile_2d_vertices = glium::VertexBuffer::dynamic (glium_display, &[ ])
141 .unwrap();
142 let tile_color_2d_vertices = glium::VertexBuffer::dynamic (glium_display, &[ ])
143 .unwrap();
144 let viewport_resources =
145 VecMap::with_capacity (render::INITIAL_VIEWPORT_VECMAP_CAPACITY);
146 let line_loop_vertices =
147 glium::VertexBuffer::dynamic (glium_display, &[ ]).unwrap();
148 Draw2d {
149 rectangle_2d_vertices,
150 pointer_vertex,
151 draw_pointer,
152 default_sprite_16x16_vertices,
153 sprite_16x16_vertices,
154 sprite_64x64_vertices,
155 rectangle_16x16_vertices,
156 rectangle_64x64_vertices,
157 rectangle_anysize_vertices,
158 tile_2d_vertices,
159 tile_color_2d_vertices,
160 viewport_resources,
161 line_loop_vertices
162 }
163 }
164
165 pub fn draw (
166 render : &Render <render::resource::Default>,
167 glium_frame : &mut glium::Frame
168 ) {
169 use color::Glsl;
170 use glium::Surface;
171 let draw2d = &render.resource.draw2d;
172 const POINTS : glium::index::IndicesSource =
174 glium::index::IndicesSource::NoIndices {
175 primitives: glium::index::PrimitiveType::Points
176 };
177 const LINE_LOOP : glium::index::IndicesSource =
178 glium::index::IndicesSource::NoIndices {
179 primitives: glium::index::PrimitiveType::LineLoop
180 };
181 for (i, viewport) in render.viewports.iter() {
183 if let Some (camera2d) = viewport.camera2d() {
184 let draw_parameters_viewport = viewport.draw_parameters();
186 let draw_parameters_blend_normal = glium::DrawParameters {
187 blend: render::params::BLEND_FUNC_NORMAL,
188 .. draw_parameters_viewport.clone()
189 };
190 let draw_parameters_blend_invert = glium::DrawParameters {
191 blend: render::params::BLEND_FUNC_INVERT_COLOR,
194 .. draw_parameters_viewport.clone()
195 };
196 let tileset_sampler_128 = render.resource.tileset_128x128_texture
198 .sampled()
199 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
200 let tileset_sampler_256 = render.resource.tileset_256x256_texture
201 .sampled()
202 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
203 let tileset_sampler = |tileset_id| match tileset_id {
204 DefaultTilesetId::EasciiAcorn128 => tileset_sampler_128,
205 DefaultTilesetId::EasciiAcorn256 => tileset_sampler_256,
206 _ => unimplemented!()
207 };
208 let default_textures_16x16_sampler =
209 render.resource.default_textures_16x16.sampled()
210 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
211 let textures_16x16_sampler = render.resource.textures_16x16.sampled()
212 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
213 let textures_64x64_sampler = render.resource.textures_64x64.sampled()
214 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
215 let (transform_mat_world_to_view, projection_mat_ortho) =
217 camera2d.view_ortho_mats();
218 let uniforms = uniform!{
220 uni_transform_mat_view: transform_mat_world_to_view,
221 uni_projection_mat_ortho: projection_mat_ortho,
222 uni_color: color::DEBUG_GREY.glsl_rgba()
223 };
224 if let Some (resources) = draw2d.viewport_resources.get (i) {
226 for draw_indices in resources.draw_indices.iter() {
227 if let Some (r) = draw_indices.rectangle.map (|r| r as usize) {
228 glium_frame.draw (
232 draw2d.rectangle_2d_vertices.slice (r..r+1).unwrap(),
233 POINTS.clone(),
234 &render.resource.shader_programs [
235 shader::ProgramId::WorldSpace2dRect as usize],
236 &uniforms,
237 &draw_parameters_blend_normal
238 ).unwrap();
239 }
241 if let Some (draw_tiles) = draw_indices.draw_tiles.as_ref() {
242 let tile_origin = draw_tiles.origin.to_world (camera2d).0.into_array();
246 let uniforms = uniform!{
247 uni_transform_mat_view: transform_mat_world_to_view,
248 uni_projection_mat_ortho: projection_mat_ortho,
249 uni_tile_space_origin: tile_origin,
251 uni_sampler2d_tileset: tileset_sampler (draw_tiles.tileset_id)
252 };
253 glium_frame.draw (
254 draw2d.tile_2d_vertices.slice (
255 draw_tiles.vertex_range.start as usize..
256 draw_tiles.vertex_range.end as usize
257 ).unwrap(),
258 POINTS.clone(),
259 &render.resource
260 .shader_programs [shader::ProgramId::TileSpace2dTile as usize],
261 &uniforms,
262 &draw_parameters_blend_invert
263 ).unwrap();
264 }
266 if let Some (draw_tiles) = draw_indices.draw_color_tiles.as_ref() {
268 let tile_origin = draw_tiles.origin.to_world (camera2d).0.into_array();
272 let uniforms = uniform!{
273 uni_transform_mat_view: transform_mat_world_to_view,
274 uni_projection_mat_ortho: projection_mat_ortho,
275 uni_tile_space_origin: tile_origin,
277 uni_sampler2d_tileset: tileset_sampler (draw_tiles.tileset_id)
278 };
279 glium_frame.draw (
280 draw2d.tile_color_2d_vertices.slice (
281 draw_tiles.vertex_range.start as usize..
282 draw_tiles.vertex_range.end as usize
283 ).unwrap(),
284 POINTS.clone(),
285 &render.resource.shader_programs [
286 shader::ProgramId::TileSpace2dTileColor as usize],
287 &uniforms,
288 &draw_parameters_blend_normal
289 ).unwrap();
290 }
292 }
293 if resources.draw_crosshair {
294 let transform_mat_world_to_view =
298 camera2d::transform_mat_world_to_view (
299 [0.0, 0.0].into(),
300 math::Rotation2::identity()
301 ).into_col_arrays();
302 let uniforms = uniform!{
303 uni_transform_mat_view: transform_mat_world_to_view,
304 uni_projection_mat_ortho: projection_mat_ortho,
305 uni_sampler2darray: default_textures_16x16_sampler
306 };
307 glium_frame.draw (
308 draw2d.default_sprite_16x16_vertices.slice (
309 (DefaultSprite16Id::Crosshair as usize)..
310 (DefaultSprite16Id::Crosshair as usize + 1)
311 ).unwrap(),
312 POINTS.clone(),
313 &render.resource.shader_programs [
314 shader::ProgramId::WorldSpace2dSpriteLayer as usize],
315 &uniforms,
316 &draw_parameters_blend_invert
317 ).unwrap();
318 }
320 if resources.draw_lineloop {
321 let base_index = i * 4;
325 glium_frame.draw (
326 draw2d.line_loop_vertices.slice (base_index..(base_index + 4))
327 .unwrap(),
328 LINE_LOOP.clone(),
329 &render.resource.shader_programs [
330 shader::ProgramId::WorldSpace2dUniColor as usize],
331 &uniforms,
332 &draw_parameters_blend_normal
333 ).unwrap();
334 }
336 } let uniforms = uniform!{
341 uni_transform_mat_view: transform_mat_world_to_view,
342 uni_projection_mat_ortho: projection_mat_ortho,
343 uni_sampler2darray: textures_16x16_sampler
344 };
345 glium_frame.draw (
346 draw2d.sprite_16x16_vertices.slice (..).unwrap(),
347 POINTS.clone(),
348 &render.resource.shader_programs [
349 shader::ProgramId::WorldSpace2dSpriteLayer as usize],
350 &uniforms,
351 &draw_parameters_blend_normal
352 ).unwrap();
353 glium_frame.draw (
357 draw2d.rectangle_16x16_vertices.slice (..).unwrap(),
358 POINTS.clone(),
359 &render.resource.shader_programs [
360 shader::ProgramId::WorldSpace2dRectUvLayer as usize],
361 &uniforms,
362 &draw_parameters_blend_normal
363 ).unwrap();
364 let uniforms = uniform!{
368 uni_transform_mat_view: transform_mat_world_to_view,
369 uni_projection_mat_ortho: projection_mat_ortho,
370 uni_sampler2darray: textures_64x64_sampler
371 };
372 glium_frame.draw (
373 draw2d.sprite_64x64_vertices.slice (..).unwrap(),
374 POINTS.clone(),
375 &render.resource.shader_programs [
376 shader::ProgramId::WorldSpace2dSpriteLayer as usize],
377 &uniforms,
378 &draw_parameters_blend_normal
379 ).unwrap();
380 glium_frame.draw (
384 draw2d.rectangle_64x64_vertices.slice (..).unwrap(),
385 POINTS.clone(),
386 &render.resource.shader_programs [
387 shader::ProgramId::WorldSpace2dRectUvLayer as usize],
388 &uniforms,
389 &draw_parameters_blend_normal
390 ).unwrap();
391 for (i, buffer) in draw2d.rectangle_anysize_vertices.iter() {
395 let uniforms = uniform!{
396 uni_transform_mat_view: transform_mat_world_to_view,
397 uni_projection_mat_ortho: projection_mat_ortho,
398 uni_sampler2d: render.resource
399 .textures_anysize.get (i).unwrap().sampled()
400 };
401 glium_frame.draw (
402 buffer,
403 POINTS.clone(),
404 &render.resource.shader_programs [
405 shader::ProgramId::WorldSpace2dRectUv as usize],
406 &uniforms,
407 &draw_parameters_blend_normal
408 ).unwrap();
409 }
410 } }
412 if let Some (buffer) = draw2d.pointer_vertex.as_ref() &&
413 let Some (texture_index) = draw2d.draw_pointer
414 {
415 let pointer_texture_sampler =
419 render.resource.textures_pointer.get (texture_index as usize).unwrap()
420 .0.sampled()
421 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
422 let (width, height) = render.glium_display.get_framebuffer_dimensions();
423 let transform_mat_world_to_view = camera2d::transform_mat_world_to_view (
424 [width as f32 / 2.0, height as f32 / 2.0].into(),
425 math::Rotation2::identity()
426 ).into_col_arrays();
427 let projection_mat_ortho = graphics::projection_mat_orthographic (
428 &Camera2d::ortho_from_viewport_zoom (width as u16, height as u16, 1.0)
429 ).into_col_arrays();
430 let draw_parameters = glium::DrawParameters {
431 blend: render::params::BLEND_FUNC_NORMAL,
432 viewport: Some (glium::Rect {
433 left: 0,
434 bottom: 0,
435 width, height
436 }),
437 .. Default::default()
438 };
439 let uniforms = uniform!{
440 uni_transform_mat_view: transform_mat_world_to_view,
441 uni_projection_mat_ortho: projection_mat_ortho,
442 uni_sampler2d: pointer_texture_sampler
443 };
444 glium_frame.draw (
445 buffer,
446 POINTS.clone(),
447 &render.resource.shader_programs [
448 shader::ProgramId::WorldSpace2dSprite as usize],
449 &uniforms,
450 &draw_parameters
451 ).unwrap();
452 }
453 }
454
455 #[inline]
456 pub fn viewport_resources_get (&self, key : usize)
457 -> Option <&ViewportResources>
458 {
459 self.viewport_resources.get (key)
460 }
461 #[inline]
466 pub fn viewport_resources_set (&mut self,
467 key : usize,
468 resources : ViewportResources
469 ) {
470 for draw_indices in resources.draw_indices.iter() {
472 draw_indices.rectangle
473 .map (|i| assert!(i as usize <= self.rectangle_2d_vertices.get_size()));
474 if let Some (draw_tiles) = draw_indices.draw_tiles.as_ref() {
475 assert!(draw_tiles.vertex_range.end as usize
476 <= self.tile_2d_vertices.get_size());
477 }
478 if let Some (draw_tiles) = draw_indices.draw_color_tiles.as_ref() {
479 assert!(draw_tiles.vertex_range.end as usize
480 <= self.tile_color_2d_vertices.get_size());
481 }
482 }
483 let _ = self.viewport_resources.insert (key, resources);
484 }
485 #[inline]
486 pub fn line_loop_vertices_set (&mut self,
487 glium_display : &glium::Display <glutin::surface::WindowSurface>,
488 vertices : &[vertex::Vert2d]
489 ) {
490 self.line_loop_vertices =
491 glium::VertexBuffer::persistent (glium_display, vertices).unwrap();
492 }
493
494 pub (crate) fn set_pointer_vertex (&mut self,
495 display : &glium::Display <glutin::surface::WindowSurface>,
496 position : math::Point2 <f32>
497 ) {
498 let vertex = vertex::Vert2d { position: position.0.into_array() };
499 if let Some (buffer) = self.pointer_vertex.as_mut() {
500 buffer.write (&[vertex]);
501 } else {
502 let buffer = glium::VertexBuffer::persistent (display, &[vertex]).unwrap();
503 self.pointer_vertex = Some (buffer);
504 }
505 }
506}
507
508impl Default for ViewportResources {
509 fn default() -> Self {
510 ViewportResources {
511 draw_indices: vec![],
512 draw_crosshair: true,
513 draw_lineloop: true
514 }
515 }
516}
517
518impl TilesOrigin {
519 pub fn to_world (&self, camera : &Camera2d) -> math::Point2 <f32> {
520 match self {
521 TilesOrigin::World => [0.0, 0.0],
522 TilesOrigin::Viewport { offset_px_left, offset_px_top } => {
523 let pixel_size_2d = 1.0 / camera.zoom();
524 [ camera.ortho().left + camera.position().0.x +
525 pixel_size_2d * *offset_px_left as f32,
526 camera.ortho().top + camera.position().0.y -
527 pixel_size_2d * *offset_px_top as f32
528 ]
529 }
530 }.into()
531 }
532}