use derive_more::{From, TryInto};
use glium::{self, uniform};
use strum::{EnumCount, EnumIter, FromRepr};
use vec_map::VecMap;
use crate::{camera2d, color, graphics, math, render, shader, vertex, Camera2d,
Render};
use super::{DefaultTexture16Id, DefaultTilesetId, PointerTextureIndexRepr};
pub struct Draw2d {
pub rectangle_2d_vertices : glium::VertexBuffer <vertex::Vert2dRectColor>,
pub line_loop_vertices : glium::VertexBuffer <vertex::Vert2d>,
pub sprite_16x16_vertices : glium::VertexBuffer <vertex::Vert2dLayer>,
pub sprite_64x64_vertices : glium::VertexBuffer <vertex::Vert2dLayer>,
pub rectangle_16x16_vertices :
glium::VertexBuffer <vertex::Vert2dRectUvLayer>,
pub rectangle_64x64_vertices :
glium::VertexBuffer <vertex::Vert2dRectUvLayer>,
pub rectangle_anysize_vertices :
VecMap <glium::VertexBuffer <vertex::Vert2dRectUv>>,
pointer_vertex : Option <glium::VertexBuffer <vertex::Vert2d>>,
pub draw_pointer : Option <PointerTextureIndexRepr>,
default_sprite_16x16_vertices : glium::VertexBuffer <vertex::Vert2dLayer>,
pub tile_2d_vertices : glium::VertexBuffer <vertex::Vert2dTile>,
pub tile_color_2d_vertices : glium::VertexBuffer <vertex::Vert2dTileColor>,
viewport_resources : VecMap <ViewportResources>,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, EnumCount, EnumIter,
FromRepr)]
#[repr(u16)]
pub enum DefaultSprite16Id {
Crosshair
}
#[derive(Clone, Debug)]
pub struct ViewportResources {
pub draw_indices : Vec <DrawIndices>,
pub draw_crosshair : bool,
pub draw_lineloop : bool
}
#[derive(Clone, Debug, Default)]
pub struct DrawIndices {
pub rectangle : Option <u32>,
pub draw_tiles : Option <Tiles>,
pub draw_color_tiles : Option <Tiles>
}
#[derive(Clone, Debug)]
pub struct Tiles {
pub vertex_range : std::ops::Range <u32>,
pub origin : TilesOrigin,
pub tileset_id : DefaultTilesetId
}
#[derive(Clone, Debug, From, TryInto)]
pub enum TilesOrigin {
Viewport {
offset_px_left : i16,
offset_px_top : i16
},
World
}
impl Draw2d {
pub fn new (glium_display : &glium::Display <glutin::surface::WindowSurface>)
-> Self
{
let rectangle_2d_vertices =
glium::VertexBuffer::dynamic (glium_display, &[ ]).unwrap();
let pointer_vertex = None;
let draw_pointer = None;
let default_sprite_16x16_vertices = glium::VertexBuffer::immutable (
glium_display,
&[vertex::Vert2dLayer {
position: [0.0, 0.0],
layer: DefaultTexture16Id::CrosshairInverse as u16
}]
).unwrap();
let sprite_16x16_vertices =
glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
let sprite_64x64_vertices =
glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
let rectangle_16x16_vertices =
glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
let rectangle_64x64_vertices =
glium::VertexBuffer::empty_immutable (glium_display, 0).unwrap();
let rectangle_anysize_vertices = VecMap::new();
let tile_2d_vertices = glium::VertexBuffer::dynamic (glium_display, &[ ])
.unwrap();
let tile_color_2d_vertices = glium::VertexBuffer::dynamic (glium_display, &[ ])
.unwrap();
let viewport_resources =
VecMap::with_capacity (render::INITIAL_VIEWPORT_VECMAP_CAPACITY);
let line_loop_vertices =
glium::VertexBuffer::dynamic (glium_display, &[ ]).unwrap();
Draw2d {
rectangle_2d_vertices,
pointer_vertex,
draw_pointer,
default_sprite_16x16_vertices,
sprite_16x16_vertices,
sprite_64x64_vertices,
rectangle_16x16_vertices,
rectangle_64x64_vertices,
rectangle_anysize_vertices,
tile_2d_vertices,
tile_color_2d_vertices,
viewport_resources,
line_loop_vertices
}
}
pub fn draw (
render : &Render <render::resource::Default>,
glium_frame : &mut glium::Frame
) {
use glium::Surface;
let draw2d = &render.resource.draw2d;
const POINTS : glium::index::IndicesSource =
glium::index::IndicesSource::NoIndices {
primitives: glium::index::PrimitiveType::Points
};
const LINE_LOOP : glium::index::IndicesSource =
glium::index::IndicesSource::NoIndices {
primitives: glium::index::PrimitiveType::LineLoop
};
for (i, viewport) in render.viewports.iter() {
if let Some (camera2d) = viewport.camera2d() {
let draw_parameters_viewport = viewport.draw_parameters();
let draw_parameters_blend_normal = glium::DrawParameters {
blend: render::params::BLEND_FUNC_NORMAL,
.. draw_parameters_viewport.clone()
};
let draw_parameters_blend_invert = glium::DrawParameters {
blend: render::params::BLEND_FUNC_INVERT_COLOR,
.. draw_parameters_viewport.clone()
};
let tileset_sampler_128 = render.resource.tileset_128x128_texture
.sampled()
.magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
let tileset_sampler_256 = render.resource.tileset_256x256_texture
.sampled()
.magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
let tileset_sampler = |tileset_id| match tileset_id {
DefaultTilesetId::EasciiAcorn128 => tileset_sampler_128,
DefaultTilesetId::EasciiAcorn256 => tileset_sampler_256,
_ => unimplemented!()
};
let default_textures_16x16_sampler =
render.resource.default_textures_16x16.sampled()
.magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
let textures_16x16_sampler = render.resource.textures_16x16.sampled()
.magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
let textures_64x64_sampler = render.resource.textures_64x64.sampled()
.magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
let (transform_mat_world_to_view, projection_mat_ortho) =
camera2d.view_ortho_mats();
let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_color: color::rgba_u8_to_rgba_f32 (color::DEBUG_GREY)
};
if let Some (resources) = draw2d.viewport_resources.get (i) {
for draw_indices in resources.draw_indices.iter() {
if let Some (r) = draw_indices.rectangle.map (|r| r as usize) {
glium_frame.draw (
draw2d.rectangle_2d_vertices.slice (r..r+1).unwrap(),
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dRect as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
}
if let Some (draw_tiles) = draw_indices.draw_tiles.as_ref() {
let tile_origin = draw_tiles.origin.to_world (camera2d).0
.into_array();
let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_tile_space_origin: tile_origin,
uni_sampler2d_tileset: tileset_sampler (draw_tiles.tileset_id)
};
glium_frame.draw (
draw2d.tile_2d_vertices.slice (
draw_tiles.vertex_range.start as usize..
draw_tiles.vertex_range.end as usize
).unwrap(),
POINTS.clone(),
&render.resource
.shader_programs [shader::ProgramId::TileSpace2dTile as usize],
&uniforms,
&draw_parameters_blend_invert
).unwrap();
}
if let Some (draw_tiles) = draw_indices.draw_color_tiles.as_ref() {
let tile_origin = draw_tiles.origin.to_world (camera2d).0
.into_array();
let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_tile_space_origin: tile_origin,
uni_sampler2d_tileset: tileset_sampler (draw_tiles.tileset_id)
};
glium_frame.draw (
draw2d.tile_color_2d_vertices.slice (
draw_tiles.vertex_range.start as usize..
draw_tiles.vertex_range.end as usize
).unwrap(),
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::TileSpace2dTileColor as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
}
}
if resources.draw_crosshair {
let transform_mat_world_to_view =
camera2d::transform_mat_world_to_view (
[0.0, 0.0].into(),
math::Rotation2::identity()
).into_col_arrays();
let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_sampler2darray: default_textures_16x16_sampler
};
glium_frame.draw (
draw2d.default_sprite_16x16_vertices.slice (
(DefaultSprite16Id::Crosshair as usize)..
(DefaultSprite16Id::Crosshair as usize + 1)
).unwrap(),
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dSpriteLayer as usize],
&uniforms,
&draw_parameters_blend_invert
).unwrap();
}
if resources.draw_lineloop {
let base_index = i * 4;
glium_frame.draw (
draw2d.line_loop_vertices.slice (base_index..(base_index + 4))
.unwrap(),
LINE_LOOP.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dUniColor as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
}
} let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_sampler2darray: textures_16x16_sampler
};
glium_frame.draw (
draw2d.sprite_16x16_vertices.slice (..).unwrap(),
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dSpriteLayer as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
glium_frame.draw (
draw2d.rectangle_16x16_vertices.slice (..).unwrap(),
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dRectUvLayer as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_sampler2darray: textures_64x64_sampler
};
glium_frame.draw (
draw2d.sprite_64x64_vertices.slice (..).unwrap(),
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dSpriteLayer as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
glium_frame.draw (
draw2d.rectangle_64x64_vertices.slice (..).unwrap(),
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dRectUvLayer as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
for (i, buffer) in draw2d.rectangle_anysize_vertices.iter() {
let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_sampler2d: render.resource
.textures_anysize.get (i).unwrap().sampled()
};
glium_frame.draw (
buffer,
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dRectUv as usize],
&uniforms,
&draw_parameters_blend_normal
).unwrap();
}
} }
if let Some (buffer) = draw2d.pointer_vertex.as_ref() &&
let Some (texture_index) = draw2d.draw_pointer
{
let pointer_texture_sampler =
render.resource.textures_pointer.get (texture_index as usize).unwrap()
.0.sampled()
.magnify_filter (glium::uniforms::MagnifySamplerFilter::Nearest);
let (width, height) = render.glium_display.get_framebuffer_dimensions();
let transform_mat_world_to_view = camera2d::transform_mat_world_to_view (
[width as f32 / 2.0, height as f32 / 2.0].into(),
math::Rotation2::identity()
).into_col_arrays();
let projection_mat_ortho = graphics::projection_mat_orthographic (
&Camera2d::ortho_from_viewport_zoom (width as u16, height as u16, 1.0)
).into_col_arrays();
let draw_parameters = glium::DrawParameters {
blend: render::params::BLEND_FUNC_NORMAL,
viewport: Some (glium::Rect {
left: 0,
bottom: 0,
width, height
}),
.. Default::default()
};
let uniforms = uniform!{
uni_transform_mat_view: transform_mat_world_to_view,
uni_projection_mat_ortho: projection_mat_ortho,
uni_sampler2d: pointer_texture_sampler
};
glium_frame.draw (
buffer,
POINTS.clone(),
&render.resource.shader_programs [
shader::ProgramId::WorldSpace2dSprite as usize],
&uniforms,
&draw_parameters
).unwrap();
}
}
#[inline]
pub fn viewport_resources_get (&self, key : usize)
-> Option <&ViewportResources>
{
self.viewport_resources.get (key)
}
#[inline]
pub fn viewport_resources_set (&mut self,
key : usize,
resources : ViewportResources
) {
for draw_indices in resources.draw_indices.iter() {
draw_indices.rectangle
.map (|i| assert!(i as usize <= self.rectangle_2d_vertices.get_size()));
if let Some (draw_tiles) = draw_indices.draw_tiles.as_ref() {
assert!(draw_tiles.vertex_range.end as usize
<= self.tile_2d_vertices.get_size());
}
if let Some (draw_tiles) = draw_indices.draw_color_tiles.as_ref() {
assert!(draw_tiles.vertex_range.end as usize
<= self.tile_color_2d_vertices.get_size());
}
}
let _ = self.viewport_resources.insert (key, resources);
}
#[inline]
pub fn line_loop_vertices_set (&mut self,
glium_display : &glium::Display <glutin::surface::WindowSurface>,
vertices : &[vertex::Vert2d]
) {
self.line_loop_vertices =
glium::VertexBuffer::persistent (glium_display, vertices).unwrap();
}
pub (crate) fn set_pointer_vertex (&mut self,
display : &glium::Display <glutin::surface::WindowSurface>,
position : math::Point2 <f32>
) {
let vertex = vertex::Vert2d { position: position.0.into_array() };
if let Some (buffer) = self.pointer_vertex.as_mut() {
buffer.write (&[vertex]);
} else {
let buffer = glium::VertexBuffer::persistent (display, &[vertex]).unwrap();
self.pointer_vertex = Some (buffer);
}
}
}
impl Default for ViewportResources {
fn default() -> Self {
ViewportResources {
draw_indices: vec![],
draw_crosshair: true,
draw_lineloop: true
}
}
}
impl TilesOrigin {
pub fn to_world (&self, camera : &Camera2d) -> math::Point2 <f32> {
match self {
TilesOrigin::World => [0.0, 0.0],
TilesOrigin::Viewport { offset_px_left, offset_px_top } => {
let pixel_size_2d = 1.0 / camera.zoom();
[ camera.ortho().left + camera.position().0.x +
pixel_size_2d * *offset_px_left as f32,
camera.ortho().top + camera.position().0.y -
pixel_size_2d * *offset_px_top as f32
]
}
}.into()
}
}