1use std::iter::FromIterator;
2use vec_map::VecMap;
3
4use nsys::{self, color, geometry, math};
5use nsys::gl::{self, glium, image};
6
7use crate::Tree;
8use crate::interface::{self, view, View};
9
10pub (crate) fn update <V : AsRef <View>> (
11 render : &mut gl::Render,
12 tileset_id : Option <gl::render::resource::DefaultTilesetId>,
13 view_tree : &Tree <V>,
14 draw_crosshair : bool
15) {
16 use gl::render::resource::{draw2d, MAIN_VIEWPORT};
17 use view::component::{Canvas, Kind};
18 log::trace!("update...");
19 let tileset_id = tileset_id.unwrap_or_default();
20 let mut rectangles = vec![];
21 let mut tiles = vec![];
22 let mut rectangles_64x64 = vec![];
23 let mut draw_indices = vec![];
24 let screen_id = view_tree.root_node_id().unwrap();
26 let mut view_ids = view_tree.traverse_pre_order_ids (&screen_id).unwrap();
27 let mut style = view::Style::default();
28 let mut draw_pointer = None;
29 view_ids.next().map (|screen_id|{
30 let screen_view = view_tree.get (&screen_id).unwrap().data().as_ref();
31 set_screen_view (render, &screen_view);
32 screen_view.appearance.style.as_ref().map (|s| style = s.clone());
35 draw_pointer = screen_view.appearance.pointer.clone();
36 let (fg, bg) = {
37 let fg = color::rgba_u8_to_rgba_f32 (style.fg.raw());
38 let bg = color::rgba_u8_to_rgba_f32 (style.bg.raw());
39 (fg, bg)
40 };
41 let canvas = Canvas::try_ref (&screen_view.component).unwrap();
42 let border_tiles = canvas_border (&canvas);
44 if !border_tiles.is_empty() {
45 let border_tiles = border_tiles.into_iter().map (
47 |gl::vertex::Vert2dTile { tile, row, column }|
48 gl::vertex::Vert2dTileColor { tile, row, column, fg, bg }
49 ).collect::<Vec<_>>();
50 let tiles_len = tiles.len() as u32;
51 draw_indices.push (draw2d::DrawIndices {
52 rectangle: None,
53 draw_tiles: None,
54 draw_color_tiles: Some (
55 draw2d::Tiles {
56 vertex_range: tiles_len..tiles_len + border_tiles.len() as u32,
57 origin: (0, 0).into(),
58 tileset_id
59 }
60 )
61 });
62 tiles.extend (border_tiles);
63 }
64 }).unwrap();
65 for id in view_ids {
66 let view = view_tree.get (&id).unwrap().data().as_ref();
67 if view.appearance.pointer.is_some() {
68 draw_pointer = view.appearance.pointer.clone();
69 }
70 match &view.component {
71 view::Component::Canvas (canvas) => {
72 let style = view.appearance.style.as_ref().unwrap_or (&style);
73 let rectangle =
74 if let Some (vertex) = canvas_background (canvas, style) {
75 rectangles.push (vertex);
76 Some (rectangles.len() as u32 - 1)
77 } else {
78 None
79 };
80 let border_tiles = canvas_border (&canvas);
81 let (body_tiles, body_rectangles_64x64) = {
82 let mut tiles = vec![];
83 let mut rectangles_64x64 = vec![];
84 for child in view_tree.children (&id).unwrap()
85 .map (|node| node.data().as_ref())
86 {
87 match child.component {
88 view::Component::Body (ref body) => {
89 let style = child.appearance.style.as_ref().unwrap_or (style);
90 tiles.extend (canvas_body (canvas, body, &style))
91 }
92 view::Component::Image (ref image) =>
93 rectangles_64x64.push (canvas_rectangle_uv (canvas, image)),
94 _ => {}
95 }
96 }
97 (tiles, rectangles_64x64)
98 };
99 let draw_color_tiles = if !border_tiles.is_empty() ||
100 !body_tiles.is_empty()
101 {
102 let (fg, bg) = {
107 let style = view.appearance.style.clone().unwrap_or_default();
108 ( color::rgba_u8_to_rgba_f32 (style.fg.raw()),
109 color::rgba_u8_to_rgba_f32 (style.bg.raw()) )
110 };
111 let border_tiles = border_tiles.into_iter().map (
112 |gl::vertex::Vert2dTile { tile, row, column }|
113 gl::vertex::Vert2dTileColor { tile, row, column, fg, bg }
114 ).collect::<Vec<_>>();
115 let tiles_len = tiles.len() as u32;
116 let vertex_range = tiles_len..tiles_len +
117 border_tiles.len() as u32 + body_tiles.len() as u32;
118 tiles.extend (border_tiles);
119 tiles.extend (body_tiles);
120 Some (draw2d::Tiles {
121 vertex_range, tileset_id, origin: (0, 0).into()
122 })
123 } else {
124 None
125 };
126 if rectangle.is_some() || draw_color_tiles.is_some() {
127 draw_indices.push (draw2d::DrawIndices {
128 rectangle,
129 draw_color_tiles,
130 draw_tiles: None
131 });
132 }
133 rectangles_64x64.extend (body_rectangles_64x64);
134 }
135 _ => {}
137 }
138 }
139 if draw_pointer.is_some() {
140 let pointer = interface::POINTER.read().unwrap();
141 let x = pointer.position_horizontal;
142 let y = pointer.position_vertical;
143 render.resource.set_pointer_position (&render.glium_display, [x, y].into())
144 }
145 render.resource.draw2d.draw_pointer = draw_pointer.map (|pointer| pointer.0);
146 render.resource.draw2d.rectangle_2d_vertices =
148 glium::VertexBuffer::dynamic (&render.glium_display, &rectangles).unwrap();
149 render.resource.draw2d.tile_color_2d_vertices =
150 glium::VertexBuffer::dynamic (&render.glium_display, &tiles).unwrap();
151 render.resource.draw2d.rectangle_64x64_vertices =
152 glium::VertexBuffer::dynamic (&render.glium_display, &rectangles_64x64)
153 .unwrap();
154 render.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT,
157 draw2d::ViewportResources {
158 draw_crosshair, draw_indices, .. draw2d::ViewportResources::default()
159 }
160 );
161 log::trace!("...update");
162}
163
164pub (crate) fn set_screen_view (render : &mut gl::Render, view : &View) {
167 use view::component::{canvas, Canvas, Kind};
168 let canvas = Canvas::try_ref (&view.component).unwrap();
169 match canvas.coordinates.dimensions() {
172 view::coordinates::Dimensions::Pixel (pixel) => {
173 let width = pixel.width();
175 let height = pixel.height();
176 render.window_resized ([
177 u32::max (width, 1),
178 u32::max (height, 1)
179 ].into());
180 render.camera2d_move_origin_to_bottom_left();
181 }
182 view::coordinates::Dimensions::Tile (_) => unreachable!()
183 }
184 let mut set_clear_color = |color|
186 render.clear_color = color::rgba_u8_to_rgba_f32 (color);
187 match canvas.clear_color {
188 canvas::ClearColor::Appearance => {
189 if let Some (style) = view.appearance.style.as_ref() {
190 set_clear_color (style.bg.raw());
191 } else {
192 log::debug!("TODO: no appearance clear color");
193 }
194 }
195 canvas::ClearColor::Fixed (Some (color)) => set_clear_color (color.raw()),
196 canvas::ClearColor::Fixed (None) => {
197 log::warn!("TODO: no fixed clear color");
198 }
199 }
200 if let Some (_border) = canvas.border.as_ref() {
202 log::warn!("TODO: screen border");
203 }
204}
205
206pub (crate) fn load_pointer (
207 render : &mut gl::Render,
208 key : gl::render::resource::PointerTextureIndexRepr,
209 bytes : &[u8],
210 offset : math::Vector2 <i16>
211) {
212 let texture = gl::texture::texture2d_with_mipmaps_from_bytes (
213 &render.glium_display,
214 bytes,
215 image::ImageFormat::Png,
216 glium::texture::MipmapsOption::NoMipmap
217 ).unwrap();
218 render.resource.textures_pointer.insert (key as usize, (texture, offset));
219}
220
221pub (crate) fn load_textures (
224 render : &mut gl::Render,
225 textures_16x16 : &[&'static str],
226 textures_64x64 : &[&'static str],
227 textures_anysize : &[&'static str]
228) {
229 if !textures_16x16.is_empty() {
231 let textures = gl::texture::texture2darray_with_mipmaps_from_files (
232 &render.glium_display,
233 textures_16x16,
234 image::ImageFormat::Png,
235 glium::texture::MipmapsOption::NoMipmap
236 ).unwrap();
237 render.resource.set_textures_16x16 (textures);
238 }
239 if !textures_64x64.is_empty() {
240 let textures = gl::texture::texture2darray_with_mipmaps_from_files (
241 &render.glium_display,
242 textures_64x64,
243 image::ImageFormat::Png,
244 glium::texture::MipmapsOption::NoMipmap
245 ).unwrap();
246 render.resource.set_textures_64x64 (textures);
247 }
248 render.resource.textures_anysize = VecMap::from_iter (textures_anysize.iter()
249 .map (|path| {
250 gl::texture::texture2d_with_mipmaps_from_file (
251 &render.glium_display,
252 path,
253 image::ImageFormat::Png,
254 glium::texture::MipmapsOption::NoMipmap
255 ).unwrap()
256 }).enumerate().map (|(i, texture)| (i+1, texture))
257 );
258}
259
260fn canvas_background (
266 canvas : &view::component::Canvas,
267 style : &view::Style
268) -> Option <gl::vertex::Vert2dRectColor> {
269 use view::coordinates;
270 use view::component::canvas;
271 let color = {
272 let color = match canvas.clear_color {
273 canvas::ClearColor::Appearance => style.bg.raw(),
274 canvas::ClearColor::Fixed (Some (color)) => color.raw(),
275 canvas::ClearColor::Fixed (None) => return None
276 };
277 color::rgba_u8_to_rgba_f32 (color)
278 };
279 let (bottom_left, dimensions) = {
280 let body_coordinates = canvas.body_coordinates();
281 if body_coordinates.dimensions().horizontal() == 0 ||
282 body_coordinates.dimensions().vertical() == 0
283 {
284 return None
285 }
286 let aabb = geometry::integer::Aabb2::from (body_coordinates);
287 let aabb_pixel = match canvas.coordinates.kind() {
288 coordinates::Kind::Tile => coordinates::tile_to_pixel_aabb (aabb),
289 coordinates::Kind::Pixel => aabb
290 };
291 ( aabb_pixel.min().numcast().unwrap().0.into_array(),
292 [aabb_pixel.width() as f32, aabb_pixel.height() as f32]
293 )
294 };
295 Some (gl::vertex::Vert2dRectColor { bottom_left, dimensions, color })
296}
297
298fn canvas_border (canvas : &view::component::Canvas)
300 -> Vec <gl::vertex::Vert2dTile>
301{
302 use gl::vertex::Vert2dTile;
303 use view::coordinates;
304 let mut tiles = vec![];
305 if let Some (border) = canvas.border.as_ref() {
306 assert!(canvas.coordinates.kind() == coordinates::Kind::Tile,
307 "TODO: support pixel borders");
308 debug_assert!(border.top <= std::u8::MAX as u32);
309 debug_assert!(border.left <= std::u8::MAX as u32);
310 debug_assert!(border.right <= std::u8::MAX as u32);
311 debug_assert!(border.bottom <= std::u8::MAX as u32);
312 debug_assert!(border.top_left <= std::u8::MAX as u32);
313 debug_assert!(border.top_right <= std::u8::MAX as u32);
314 debug_assert!(border.bottom_left <= std::u8::MAX as u32);
315 debug_assert!(border.bottom_right <= std::u8::MAX as u32);
316 let [row, column] = [
317 canvas.coordinates.position_vertical(),
318 canvas.coordinates.position_horizontal()
319 ];
320 let [rows, columns] = [
321 canvas.coordinates.dimensions_vertical() as i32,
322 canvas.coordinates.dimensions_horizontal() as i32
323 ];
324 if rows > 0 && columns > 0 {
325 let (thickness_top, thickness_bottom, thickness_left, thickness_right) = (
326 border.thickness_top as i32,
327 border.thickness_bottom as i32,
328 border.thickness_left as i32,
329 border.thickness_right as i32
330 );
331 let mut push_tile = |row, column, tile|
332 tiles.push (Vert2dTile { row, column, tile: tile as u8 });
333 for r in row..row+thickness_top {
335 for c in column..column+thickness_left {
336 push_tile (r, c, border.top_left);
337 }
338 for c in column+thickness_left..column+columns-thickness_right {
339 push_tile (r, c, border.top);
340 }
341 for c in column+columns-thickness_right..column+columns {
342 push_tile (r, c, border.top_right);
343 }
344 }
345 for r in row+thickness_top..row+rows-thickness_bottom {
347 for c in column..column+thickness_left {
348 push_tile (r, c, border.left);
349 }
350 for c in column+columns-thickness_right..column+columns {
351 push_tile (r, c, border.right);
352 }
353 }
354 for r in row+rows-thickness_bottom..row+rows {
356 for c in column..column+thickness_left {
357 push_tile (r, c, border.bottom_left);
358 }
359 for c in column+thickness_left..column+columns-thickness_right {
360 push_tile (r, c, border.bottom);
361 }
362 for c in column+columns-thickness_right..column+columns {
363 push_tile (r, c, border.bottom_right);
364 }
365 }
366 }
367 }
368 tiles
369}
370
371fn canvas_body (
373 canvas : &view::component::Canvas,
374 body : &view::component::Body,
375 style : &view::Style
376) -> Vec <gl::vertex::Vert2dTileColor> {
377 use gl::vertex::Vert2dTileColor;
378 let (fg, bg) = {
379 let fg = color::rgba_u8_to_rgba_f32 (style.fg.raw());
380 let bg = color::rgba_u8_to_rgba_f32 (style.bg.raw());
381 (fg, bg)
382 };
383 let mut tiles = vec![];
384 let (rc_min, _rc_max) = {
385 let [row, col] = [
386 canvas.coordinates.position_vertical(),
387 canvas.coordinates.position_horizontal()
388 ];
389 let [rows, cols] = [
390 canvas.coordinates.dimensions_vertical() as i32,
391 canvas.coordinates.dimensions_horizontal() as i32
392 ];
393 ( (row, col),
394 (row + rows-1, col + cols-1) )
395 };
396 let (border_thickness_top, border_thickness_left) =
397 canvas.border.as_ref().map (|border|
398 ( border.thickness_top as i32,
399 border.thickness_left as i32 )
400 ).unwrap_or_default();
401 for (i, line) in body.0.lines().enumerate() {
402 let row = rc_min.0 + border_thickness_top + i as i32;
403 for (j, ch) in line.chars().enumerate().skip_while (|(_, c)| *c == '\0') {
404 let column = rc_min.1 + border_thickness_left + j as i32;
405 tiles.push (Vert2dTileColor {
406 row, column, tile: ch as u8, fg, bg
407 });
408 }
409 }
410 tiles
411}
412
413fn canvas_rectangle_uv (
415 canvas : &view::component::Canvas,
416 image : &view::component::Image
417) -> gl::vertex::Vert2dRectUvLayer {
418 use view::coordinates;
419 let (_resource, layer) = match image {
420 view::component::Image::Texture (view::Texture { resource, index }) =>
421 (*resource, *index),
422 _ => unimplemented!()
423 };
424 let (bottom_left, dimensions) = {
425 let body_coordinates = canvas.body_coordinates();
426 let horizontal = body_coordinates.position_horizontal();
427 let vertical = body_coordinates.position_vertical();
428 let width = body_coordinates.dimensions_horizontal();
429 let height = body_coordinates.dimensions_vertical();
430 let ([x, y], [w, h]) = if
431 body_coordinates.kind() == coordinates::Kind::Tile
432 {
433 let [tile_w, tile_h] = *coordinates::TILE_WH;
434 ( coordinates::tile_to_pixel (vertical + height as i32, horizontal),
435 [width * tile_w, height * tile_h]
436 )
437 } else {
438 ([horizontal, vertical], [width, height])
439 };
440 ([x as f32, y as f32], [w as f32, h as f32])
441 };
442 let uv = [0.0, 0.0];
444 gl::vertex::Vert2dRectUvLayer { bottom_left, dimensions, uv, layer }
445}