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 glium::Surface;
170 let draw2d = &render.resource.draw2d;
171 const POINTS : glium::index::IndicesSource =
173 glium::index::IndicesSource::NoIndices {
174 primitives: glium::index::PrimitiveType::Points
175 };
176 const LINE_LOOP : glium::index::IndicesSource =
177 glium::index::IndicesSource::NoIndices {
178 primitives: glium::index::PrimitiveType::LineLoop
179 };
180 for (i, viewport) in render.viewports.iter() {
182 if let Some (camera2d) = viewport.camera2d() {
183 let draw_parameters_viewport = viewport.draw_parameters();
185 let draw_parameters_blend_normal = glium::DrawParameters {
186 blend: render::params::BLEND_FUNC_NORMAL,
187 .. draw_parameters_viewport.clone()
188 };
189 let draw_parameters_blend_invert = glium::DrawParameters {
190 blend: render::params::BLEND_FUNC_INVERT_COLOR,
193 .. draw_parameters_viewport.clone()
194 };
195 let tileset_sampler_128 = render.resource.tileset_128x128_texture
197 .sampled()
198 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
199 let tileset_sampler_256 = render.resource.tileset_256x256_texture
200 .sampled()
201 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
202 let tileset_sampler = |tileset_id| match tileset_id {
203 DefaultTilesetId::EasciiAcorn128 => tileset_sampler_128,
204 DefaultTilesetId::EasciiAcorn256 => tileset_sampler_256,
205 _ => unimplemented!()
206 };
207 let default_textures_16x16_sampler =
208 render.resource.default_textures_16x16.sampled()
209 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
210 let textures_16x16_sampler = render.resource.textures_16x16.sampled()
211 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
212 let textures_64x64_sampler = render.resource.textures_64x64.sampled()
213 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
214 let (transform_mat_world_to_view, projection_mat_ortho) =
216 camera2d.view_ortho_mats();
217 let uniforms = uniform!{
219 uni_transform_mat_view: transform_mat_world_to_view,
220 uni_projection_mat_ortho: projection_mat_ortho,
221 uni_color: color::rgba_u8_to_rgba_f32 (color::DEBUG_GREY)
222 };
223 if let Some (resources) = draw2d.viewport_resources.get (i) {
225 for draw_indices in resources.draw_indices.iter() {
226 if let Some (r) = draw_indices.rectangle.map (|r| r as usize) {
227 glium_frame.draw (
231 draw2d.rectangle_2d_vertices.slice (r..r+1).unwrap(),
232 POINTS.clone(),
233 &render.resource.shader_programs [
234 shader::ProgramId::WorldSpace2dRect as usize],
235 &uniforms,
236 &draw_parameters_blend_normal
237 ).unwrap();
238 }
240 if let Some (draw_tiles) = draw_indices.draw_tiles.as_ref() {
241 let tile_origin = draw_tiles.origin.to_world (camera2d).0
245 .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
272 .into_array();
273 let uniforms = uniform!{
274 uni_transform_mat_view: transform_mat_world_to_view,
275 uni_projection_mat_ortho: projection_mat_ortho,
276 uni_tile_space_origin: tile_origin,
278 uni_sampler2d_tileset: tileset_sampler (draw_tiles.tileset_id)
279 };
280 glium_frame.draw (
281 draw2d.tile_color_2d_vertices.slice (
282 draw_tiles.vertex_range.start as usize..
283 draw_tiles.vertex_range.end as usize
284 ).unwrap(),
285 POINTS.clone(),
286 &render.resource.shader_programs [
287 shader::ProgramId::TileSpace2dTileColor as usize],
288 &uniforms,
289 &draw_parameters_blend_normal
290 ).unwrap();
291 }
293 }
294 if resources.draw_crosshair {
295 let transform_mat_world_to_view =
299 camera2d::transform_mat_world_to_view (
300 [0.0, 0.0].into(),
301 math::Rotation2::identity()
302 ).into_col_arrays();
303 let uniforms = uniform!{
304 uni_transform_mat_view: transform_mat_world_to_view,
305 uni_projection_mat_ortho: projection_mat_ortho,
306 uni_sampler2darray: default_textures_16x16_sampler
307 };
308 glium_frame.draw (
309 draw2d.default_sprite_16x16_vertices.slice (
310 (DefaultSprite16Id::Crosshair as usize)..
311 (DefaultSprite16Id::Crosshair as usize + 1)
312 ).unwrap(),
313 POINTS.clone(),
314 &render.resource.shader_programs [
315 shader::ProgramId::WorldSpace2dSpriteLayer as usize],
316 &uniforms,
317 &draw_parameters_blend_invert
318 ).unwrap();
319 }
321 if resources.draw_lineloop {
322 let base_index = i * 4;
326 glium_frame.draw (
327 draw2d.line_loop_vertices.slice (base_index..(base_index + 4))
328 .unwrap(),
329 LINE_LOOP.clone(),
330 &render.resource.shader_programs [
331 shader::ProgramId::WorldSpace2dUniColor as usize],
332 &uniforms,
333 &draw_parameters_blend_normal
334 ).unwrap();
335 }
337 } let uniforms = uniform!{
342 uni_transform_mat_view: transform_mat_world_to_view,
343 uni_projection_mat_ortho: projection_mat_ortho,
344 uni_sampler2darray: textures_16x16_sampler
345 };
346 glium_frame.draw (
347 draw2d.sprite_16x16_vertices.slice (..).unwrap(),
348 POINTS.clone(),
349 &render.resource.shader_programs [
350 shader::ProgramId::WorldSpace2dSpriteLayer as usize],
351 &uniforms,
352 &draw_parameters_blend_normal
353 ).unwrap();
354 glium_frame.draw (
358 draw2d.rectangle_16x16_vertices.slice (..).unwrap(),
359 POINTS.clone(),
360 &render.resource.shader_programs [
361 shader::ProgramId::WorldSpace2dRectUvLayer as usize],
362 &uniforms,
363 &draw_parameters_blend_normal
364 ).unwrap();
365 let uniforms = uniform!{
369 uni_transform_mat_view: transform_mat_world_to_view,
370 uni_projection_mat_ortho: projection_mat_ortho,
371 uni_sampler2darray: textures_64x64_sampler
372 };
373 glium_frame.draw (
374 draw2d.sprite_64x64_vertices.slice (..).unwrap(),
375 POINTS.clone(),
376 &render.resource.shader_programs [
377 shader::ProgramId::WorldSpace2dSpriteLayer as usize],
378 &uniforms,
379 &draw_parameters_blend_normal
380 ).unwrap();
381 glium_frame.draw (
385 draw2d.rectangle_64x64_vertices.slice (..).unwrap(),
386 POINTS.clone(),
387 &render.resource.shader_programs [
388 shader::ProgramId::WorldSpace2dRectUvLayer as usize],
389 &uniforms,
390 &draw_parameters_blend_normal
391 ).unwrap();
392 for (i, buffer) in draw2d.rectangle_anysize_vertices.iter() {
396 let uniforms = uniform!{
397 uni_transform_mat_view: transform_mat_world_to_view,
398 uni_projection_mat_ortho: projection_mat_ortho,
399 uni_sampler2d: render.resource
400 .textures_anysize.get (i).unwrap().sampled()
401 };
402 glium_frame.draw (
403 buffer,
404 POINTS.clone(),
405 &render.resource.shader_programs [
406 shader::ProgramId::WorldSpace2dRectUv as usize],
407 &uniforms,
408 &draw_parameters_blend_normal
409 ).unwrap();
410 }
411 } }
413 if let Some (buffer) = draw2d.pointer_vertex.as_ref() &&
414 let Some (texture_index) = draw2d.draw_pointer
415 {
416 let pointer_texture_sampler =
420 render.resource.textures_pointer.get (texture_index as usize).unwrap()
421 .0.sampled()
422 .magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
423 let (width, height) = render.glium_display.get_framebuffer_dimensions();
424 let transform_mat_world_to_view = camera2d::transform_mat_world_to_view (
425 [width as f32 / 2.0, height as f32 / 2.0].into(),
426 math::Rotation2::identity()
427 ).into_col_arrays();
428 let projection_mat_ortho = graphics::projection_mat_orthographic (
429 &Camera2d::ortho_from_viewport_zoom (width as u16, height as u16, 1.0)
430 ).into_col_arrays();
431 let draw_parameters = glium::DrawParameters {
432 blend: render::params::BLEND_FUNC_NORMAL,
433 viewport: Some (glium::Rect {
434 left: 0,
435 bottom: 0,
436 width, height
437 }),
438 .. Default::default()
439 };
440 let uniforms = uniform!{
441 uni_transform_mat_view: transform_mat_world_to_view,
442 uni_projection_mat_ortho: projection_mat_ortho,
443 uni_sampler2d: pointer_texture_sampler
444 };
445 glium_frame.draw (
446 buffer,
447 POINTS.clone(),
448 &render.resource.shader_programs [
449 shader::ProgramId::WorldSpace2dSprite as usize],
450 &uniforms,
451 &draw_parameters
452 ).unwrap();
453 }
454 }
455
456 #[inline]
457 pub fn viewport_resources_get (&self, key : usize)
458 -> Option <&ViewportResources>
459 {
460 self.viewport_resources.get (key)
461 }
462 #[inline]
467 pub fn viewport_resources_set (&mut self,
468 key : usize,
469 resources : ViewportResources
470 ) {
471 for draw_indices in resources.draw_indices.iter() {
473 draw_indices.rectangle
474 .map (|i| assert!(i as usize <= self.rectangle_2d_vertices.get_size()));
475 if let Some (draw_tiles) = draw_indices.draw_tiles.as_ref() {
476 assert!(draw_tiles.vertex_range.end as usize
477 <= self.tile_2d_vertices.get_size());
478 }
479 if let Some (draw_tiles) = draw_indices.draw_color_tiles.as_ref() {
480 assert!(draw_tiles.vertex_range.end as usize
481 <= self.tile_color_2d_vertices.get_size());
482 }
483 }
484 let _ = self.viewport_resources.insert (key, resources);
485 }
486 #[inline]
487 pub fn line_loop_vertices_set (&mut self,
488 glium_display : &glium::Display <glutin::surface::WindowSurface>,
489 vertices : &[vertex::Vert2d]
490 ) {
491 self.line_loop_vertices =
492 glium::VertexBuffer::persistent (glium_display, vertices).unwrap();
493 }
494
495 pub (crate) fn set_pointer_vertex (&mut self,
496 display : &glium::Display <glutin::surface::WindowSurface>,
497 position : math::Point2 <f32>
498 ) {
499 let vertex = vertex::Vert2d { position: position.0.into_array() };
500 if let Some (buffer) = self.pointer_vertex.as_mut() {
501 buffer.write (&[vertex]);
502 } else {
503 let buffer = glium::VertexBuffer::persistent (display, &[vertex]).unwrap();
504 self.pointer_vertex = Some (buffer);
505 }
506 }
507}
508
509impl Default for ViewportResources {
510 fn default() -> Self {
511 ViewportResources {
512 draw_indices: vec![],
513 draw_crosshair: true,
514 draw_lineloop: true
515 }
516 }
517}
518
519impl TilesOrigin {
520 pub fn to_world (&self, camera : &Camera2d) -> math::Point2 <f32> {
521 match self {
522 TilesOrigin::World => [0.0, 0.0],
523 TilesOrigin::Viewport { offset_px_left, offset_px_top } => {
524 let pixel_size_2d = 1.0 / camera.zoom();
525 [ camera.ortho().left + camera.position().0.x +
526 pixel_size_2d * *offset_px_left as f32,
527 camera.ortho().top + camera.position().0.y -
528 pixel_size_2d * *offset_px_top as f32
529 ]
530 }
531 }.into()
532 }
533}