use std::iter::FromIterator;
use vec_map::VecMap;
use nsys::{self, geometry, math};
use nsys::color::{self, Color};
use nsys::gl::{self, glium, image};
use crate::Tree;
use crate::interface::{self, view, View};
pub (crate) fn update <V : AsRef <View>> (
render : &mut gl::Render,
tileset_id : Option <gl::render::resource::DefaultTilesetId>,
view_tree : &Tree <V>,
draw_crosshair : bool
) {
use color::Normalize;
use gl::render::resource::{draw2d, MAIN_VIEWPORT};
use view::component::{Canvas, Kind};
log::trace!("update...");
let tileset_id = tileset_id.unwrap_or_default();
let mut rectangles = vec![];
let mut tiles = vec![];
let mut rectangles_64x64 = vec![];
let mut draw_indices = vec![];
let screen_id = view_tree.root_node_id().unwrap();
let mut view_ids = view_tree.traverse_pre_order_ids (screen_id).unwrap();
let mut style = view::Style::default();
let mut draw_pointer = None;
view_ids.next().map (|screen_id|{
let screen_view = view_tree.get (&screen_id).unwrap().data().as_ref();
set_screen_view (render, screen_view);
screen_view.appearance.style.as_ref().map (|s| style = s.clone());
draw_pointer = screen_view.appearance.pointer.clone();
let (fg, bg) = {
let fg = style.fg.raw().normalize().into();
let bg = style.bg.raw().normalize().into();
(fg, bg)
};
let canvas = Canvas::try_ref (&screen_view.component).unwrap();
let border_tiles = canvas_border (canvas);
if !border_tiles.is_empty() {
let border_tiles = border_tiles.into_iter().map (
|gl::vertex::Vert2dTile { tile, row, column }|
gl::vertex::Vert2dTileColor { tile, row, column, fg, bg }
).collect::<Vec<_>>();
let tiles_len = tiles.len() as u32;
draw_indices.push (draw2d::DrawIndices {
rectangle: None,
draw_tiles: None,
draw_color_tiles: Some (
draw2d::Tiles {
vertex_range: tiles_len..tiles_len + border_tiles.len() as u32,
origin: (0, 0).into(),
tileset_id
}
)
});
tiles.extend (border_tiles);
}
}).unwrap();
for id in view_ids {
let view = view_tree.get (&id).unwrap().data().as_ref();
if view.appearance.pointer.is_some() {
draw_pointer = view.appearance.pointer.clone();
}
match &view.component {
view::Component::Canvas (canvas) => {
let style = view.appearance.style.as_ref().unwrap_or (&style);
let rectangle =
if let Some (vertex) = canvas_background (canvas, style) {
rectangles.push (vertex);
Some (rectangles.len() as u32 - 1)
} else {
None
};
let border_tiles = canvas_border (canvas);
let (body_tiles, body_rectangles_64x64) = {
let mut tiles = vec![];
let mut rectangles_64x64 = vec![];
for child in view_tree.children (&id).unwrap()
.map (|node| node.data().as_ref())
{
match &child.component {
view::Component::Body (body) => {
let style = child.appearance.style.as_ref().unwrap_or (style);
tiles.extend (canvas_body (canvas, body, style))
}
view::Component::Image (image) =>
rectangles_64x64.push (canvas_rectangle_uv (canvas, image)),
_ => {}
}
}
(tiles, rectangles_64x64)
};
let draw_color_tiles = if !border_tiles.is_empty() || !body_tiles.is_empty() {
let (fg, bg) = {
let style = view.appearance.style.clone().unwrap_or_default();
( style.fg.raw().normalize().into(),
style.bg.raw().normalize().into() )
};
let border_tiles = border_tiles.into_iter().map (
|gl::vertex::Vert2dTile { tile, row, column }|
gl::vertex::Vert2dTileColor { tile, row, column, fg, bg }
).collect::<Vec<_>>();
let tiles_len = tiles.len() as u32;
let vertex_range = tiles_len..tiles_len
+ border_tiles.len() as u32 + body_tiles.len() as u32;
tiles.extend (border_tiles);
tiles.extend (body_tiles);
Some (draw2d::Tiles {
vertex_range, tileset_id, origin: (0, 0).into()
})
} else {
None
};
if rectangle.is_some() || draw_color_tiles.is_some() {
draw_indices.push (draw2d::DrawIndices {
rectangle,
draw_color_tiles,
draw_tiles: None
});
}
rectangles_64x64.extend (body_rectangles_64x64);
}
_ => {}
}
}
if draw_pointer.is_some() {
let (x, y) = {
let pointer = interface::POINTER.read().unwrap();
(pointer.position_horizontal, pointer.position_vertical)
};
render.resource.set_pointer_position (&render.glium_display, [x, y].into())
}
render.resource.draw2d.draw_pointer = draw_pointer.map (|pointer| pointer.0);
render.resource.draw2d.rectangle_2d_vertices =
glium::VertexBuffer::dynamic (&render.glium_display, &rectangles).unwrap();
render.resource.draw2d.tile_color_2d_vertices =
glium::VertexBuffer::dynamic (&render.glium_display, &tiles).unwrap();
render.resource.draw2d.rectangle_64x64_vertices =
glium::VertexBuffer::dynamic (&render.glium_display, &rectangles_64x64).unwrap();
render.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT,
draw2d::ViewportResources {
draw_crosshair, draw_indices, .. draw2d::ViewportResources::default()
}
);
log::trace!("...update");
}
pub (crate) fn set_screen_view (render : &mut gl::Render, view : &View) {
use color::Normalize;
use view::component::{canvas, Canvas, Kind};
let canvas = Canvas::try_ref (&view.component).unwrap();
match canvas.coordinates.dimensions() {
view::coordinates::Dimensions::Pixel (pixel) => {
let width = pixel.width();
let height = pixel.height();
render.window_resized ([
u32::max (width, 1),
u32::max (height, 1)
].into());
render.camera2d_move_origin_to_bottom_left();
}
view::coordinates::Dimensions::Tile (_) => unreachable!()
}
let mut set_clear_color = |color : Color|
render.clear_color = color.raw().normalize().into();
match canvas.clear_color {
canvas::ClearColor::Appearance =>
if let Some (style) = view.appearance.style.as_ref() {
set_clear_color (style.bg);
} else {
log::debug!("TODO: no appearance clear color");
}
canvas::ClearColor::Fixed (Some (color)) => set_clear_color (color),
canvas::ClearColor::Fixed (None) => log::warn!("TODO: no fixed clear color")
}
if let Some (_border) = canvas.border.as_ref() {
log::warn!("TODO: screen border");
}
}
pub (crate) fn load_pointer (
render : &mut gl::Render,
key : gl::render::resource::PointerTextureIndexRepr,
bytes : &[u8],
offset : math::Vector2 <i16>
) {
let texture = gl::texture::texture2d_with_mipmaps_from_bytes (
&render.glium_display,
bytes,
image::ImageFormat::Png,
glium::texture::MipmapsOption::NoMipmap
).unwrap();
render.resource.textures_pointer.insert (key as usize, (texture, offset));
}
pub (crate) fn load_textures (
render : &mut gl::Render,
textures_16x16 : &[&'static str],
textures_64x64 : &[&'static str],
textures_anysize : &[&'static str]
) {
if !textures_16x16.is_empty() {
let textures = gl::texture::texture2darray_with_mipmaps_from_files (
&render.glium_display,
textures_16x16,
image::ImageFormat::Png,
glium::texture::MipmapsOption::NoMipmap
).unwrap();
render.resource.set_textures_16x16 (textures);
}
if !textures_64x64.is_empty() {
let textures = gl::texture::texture2darray_with_mipmaps_from_files (
&render.glium_display,
textures_64x64,
image::ImageFormat::Png,
glium::texture::MipmapsOption::NoMipmap
).unwrap();
render.resource.set_textures_64x64 (textures);
}
render.resource.textures_anysize = VecMap::from_iter (textures_anysize.iter()
.map (|path| {
gl::texture::texture2d_with_mipmaps_from_file (
&render.glium_display,
path,
image::ImageFormat::Png,
glium::texture::MipmapsOption::NoMipmap
).unwrap()
}).enumerate().map (|(i, texture)| (i+1, texture))
);
}
fn canvas_background (
canvas : &view::component::Canvas,
style : &view::Style
) -> Option <gl::vertex::Vert2dRectColor> {
use color::Normalize;
use view::coordinates;
use view::component::canvas;
let color = {
let color = match canvas.clear_color {
canvas::ClearColor::Appearance => style.bg,
canvas::ClearColor::Fixed (Some (color)) => color,
canvas::ClearColor::Fixed (None) => return None
};
color.raw().normalize().into()
};
let (bottom_left, dimensions) = {
let body_coordinates = canvas.body_coordinates();
if body_coordinates.dimensions().horizontal() == 0
|| body_coordinates.dimensions().vertical() == 0
{
return None
}
let aabb = geometry::integer::Aabb2::from (body_coordinates);
let aabb_pixel = match canvas.coordinates.kind() {
coordinates::Kind::Tile => coordinates::tile_to_pixel_aabb (aabb),
coordinates::Kind::Pixel => aabb
};
( aabb_pixel.min().numcast().unwrap().0.into_array(),
[*aabb_pixel.width() as f32, *aabb_pixel.height() as f32]
)
};
Some (gl::vertex::Vert2dRectColor { bottom_left, dimensions, color })
}
fn canvas_border (canvas : &view::component::Canvas) -> Vec <gl::vertex::Vert2dTile> {
use gl::vertex::Vert2dTile;
use view::coordinates;
let mut tiles = vec![];
if let Some (border) = canvas.border.as_ref() {
assert!(canvas.coordinates.kind() == coordinates::Kind::Tile,
"TODO: support pixel borders");
debug_assert!(border.top <= u8::MAX as u32);
debug_assert!(border.left <= u8::MAX as u32);
debug_assert!(border.right <= u8::MAX as u32);
debug_assert!(border.bottom <= u8::MAX as u32);
debug_assert!(border.top_left <= u8::MAX as u32);
debug_assert!(border.top_right <= u8::MAX as u32);
debug_assert!(border.bottom_left <= u8::MAX as u32);
debug_assert!(border.bottom_right <= u8::MAX as u32);
let [row, column] = [
canvas.coordinates.position_vertical(),
canvas.coordinates.position_horizontal()
];
let [rows, columns] = [
canvas.coordinates.dimensions_vertical() as i32,
canvas.coordinates.dimensions_horizontal() as i32
];
if rows > 0 && columns > 0 {
let (thickness_top, thickness_bottom, thickness_left, thickness_right) = (
border.thickness_top as i32,
border.thickness_bottom as i32,
border.thickness_left as i32,
border.thickness_right as i32
);
let mut push_tile = |row, column, tile|
tiles.push (Vert2dTile { row, column, tile: tile as u8 });
for r in row..row+thickness_top {
for c in column..column+thickness_left {
push_tile (r, c, border.top_left);
}
for c in column+thickness_left..column+columns-thickness_right {
push_tile (r, c, border.top);
}
for c in column+columns-thickness_right..column+columns {
push_tile (r, c, border.top_right);
}
}
for r in row+thickness_top..row+rows-thickness_bottom {
for c in column..column+thickness_left {
push_tile (r, c, border.left);
}
for c in column+columns-thickness_right..column+columns {
push_tile (r, c, border.right);
}
}
for r in row+rows-thickness_bottom..row+rows {
for c in column..column+thickness_left {
push_tile (r, c, border.bottom_left);
}
for c in column+thickness_left..column+columns-thickness_right {
push_tile (r, c, border.bottom);
}
for c in column+columns-thickness_right..column+columns {
push_tile (r, c, border.bottom_right);
}
}
}
}
tiles
}
fn canvas_body (
canvas : &view::component::Canvas,
body : &view::component::Body,
style : &view::Style
) -> Vec <gl::vertex::Vert2dTileColor> {
use color::Normalize;
use gl::vertex::Vert2dTileColor;
let (fg, bg) = {
let fg = style.fg.raw().normalize().into();
let bg = style.bg.raw().normalize().into();
(fg, bg)
};
let mut tiles = vec![];
let (rc_min, _rc_max) = {
let [row, col] = [
canvas.coordinates.position_vertical(),
canvas.coordinates.position_horizontal()
];
let [rows, cols] = [
canvas.coordinates.dimensions_vertical() as i32,
canvas.coordinates.dimensions_horizontal() as i32
];
( (row, col),
(row + rows-1, col + cols-1) )
};
let (border_thickness_top, border_thickness_left) =
canvas.border.as_ref().map (|border|
( border.thickness_top as i32,
border.thickness_left as i32 )
).unwrap_or_default();
for (i, line) in body.0.lines().enumerate() {
let row = rc_min.0 + border_thickness_top + i as i32;
for (j, ch) in line.chars().enumerate().skip_while (|(_, c)| *c == '\0') {
let column = rc_min.1 + border_thickness_left + j as i32;
tiles.push (Vert2dTileColor {
row, column, tile: ch as u8, fg, bg
});
}
}
tiles
}
fn canvas_rectangle_uv (
canvas : &view::component::Canvas,
image : &view::component::Image
) -> gl::vertex::Vert2dRectUvLayer {
use view::coordinates;
let (_resource, layer) = match image {
view::component::Image::Texture (view::Texture { resource, index }) =>
(*resource, *index),
_ => unimplemented!()
};
let (bottom_left, dimensions) = {
let body_coordinates = canvas.body_coordinates();
let horizontal = body_coordinates.position_horizontal();
let vertical = body_coordinates.position_vertical();
let width = body_coordinates.dimensions_horizontal();
let height = body_coordinates.dimensions_vertical();
let ([x, y], [w, h]) = if body_coordinates.kind() == coordinates::Kind::Tile {
let [tile_w, tile_h] = *coordinates::TILE_WH;
( coordinates::tile_to_pixel (vertical + height as i32, horizontal),
[width * tile_w, height * tile_h]
)
} else {
([horizontal, vertical], [width, height])
};
([x as f32, y as f32], [w as f32, h as f32])
};
let uv = [0.0, 0.0];
gl::vertex::Vert2dRectUvLayer { bottom_left, dimensions, uv, layer }
}