1mod defaults;
2mod primitive;
3pub mod style;
4mod widget;
5
6pub use defaults::Defaults;
7
8use primitive::Primitive;
9
10use super::{
11 super::graphic::{self, Graphic, TexId},
12 Font, FontId, RawFont, Rotation,
13 cache::Cache,
14 widget::image,
15};
16use crate::{
17 error::Error,
18 render::{
19 DynamicModel, Mesh, Renderer, UiBoundLocals, UiDrawer, UiLocals, UiMode, UiVertex,
20 create_ui_quad, create_ui_quad_vert_gradient,
21 },
22};
23use common::{slowjob::SlowJobPool, util::srgba_to_linear};
24use common_base::span;
25use std::{convert::TryInto, ops::Range};
26use vek::*;
27
28enum DrawKind {
29 Image(TexId),
30 Plain,
32}
33
34#[expect(dead_code)] enum DrawCommand {
36 Draw { kind: DrawKind, verts: Range<u32> },
37 Scissor(Aabr<u16>),
38 WorldPos(Option<usize>),
39}
40impl DrawCommand {
41 fn image(verts: Range<usize>, id: TexId) -> DrawCommand {
42 DrawCommand::Draw {
43 kind: DrawKind::Image(id),
44 verts: verts
47 .start
48 .try_into()
49 .expect("Vertex count for UI rendering does not fit in a u32!")
50 ..verts
51 .end
52 .try_into()
53 .expect("Vertex count for UI rendering does not fit in a u32!"),
54 }
55 }
56
57 fn plain(verts: Range<usize>) -> DrawCommand {
58 DrawCommand::Draw {
59 kind: DrawKind::Plain,
60 verts: verts
61 .start
62 .try_into()
63 .expect("Vertex count for UI rendering does not fit in a u32!")
64 ..verts
65 .end
66 .try_into()
67 .expect("Vertex count for UI rendering does not fit in a u32!"),
68 }
69 }
70}
71
72#[derive(PartialEq)]
73enum State {
74 Image(TexId),
75 Plain,
76}
77
78pub struct IcedRenderer {
83 cache: Cache,
85 model: DynamicModel<UiVertex>,
87 ingame_locals: Vec<UiBoundLocals>,
89 interface_locals: UiBoundLocals,
91
92 half_res: Vec2<f32>,
96 align: Vec2<f32>,
98 p_scale: f32,
100 win_dims: Vec2<f32>,
102 window_scissor: Aabr<u16>,
104
105 current_state: State,
107 mesh: Mesh<UiVertex>,
108 glyphs: Vec<(usize, usize, Rgba<f32>, Vec2<u32>)>,
109 last_glyph_verts: Vec<(Aabr<f32>, Aabr<f32>)>,
113 start: usize,
114 draw_commands: Vec<DrawCommand>,
116}
117impl IcedRenderer {
118 pub fn new(
119 renderer: &mut Renderer,
120 scaled_resolution: Vec2<f32>,
121 physical_resolution: Vec2<u32>,
122 default_font: Font,
123 ) -> Result<Self, Error> {
124 let (half_res, align, p_scale) =
125 Self::calculate_resolution_dependents(physical_resolution, scaled_resolution);
126
127 let interface_locals = renderer.create_ui_bound_locals(&[UiLocals::default()]);
128
129 Ok(Self {
130 cache: Cache::new(renderer, default_font)?,
131 draw_commands: Vec::new(),
132 model: renderer.create_dynamic_model(100),
133 interface_locals,
134 ingame_locals: Vec::new(),
135 mesh: Mesh::new(),
136 glyphs: Vec::new(),
137 last_glyph_verts: Vec::new(),
138 current_state: State::Plain,
139 half_res,
140 align,
141 p_scale,
142 win_dims: scaled_resolution,
143 window_scissor: default_scissor(physical_resolution),
144 start: 0,
145 })
146 }
147
148 pub fn add_font(&mut self, font: RawFont) -> FontId { self.cache.add_font(font) }
149
150 pub fn clear_fonts(&mut self, default_font: Font) { self.cache.clear_fonts(default_font); }
152
153 pub fn add_graphic(&mut self, graphic: Graphic) -> graphic::Id {
154 self.cache.add_graphic(graphic)
155 }
156
157 pub fn replace_graphic(&mut self, id: graphic::Id, graphic: Graphic) {
158 self.cache.replace_graphic(id, graphic);
159 }
160
161 fn image_dims(&self, handle: image::Handle) -> (u32, u32) {
162 self
163 .cache
164 .graphic_cache()
165 .get_graphic_dims((handle, Rotation::None))
166 .unwrap()
168 }
169
170 pub fn resize(
171 &mut self,
172 scaled_resolution: Vec2<f32>,
173 physical_resolution: Vec2<u32>,
174 renderer: &mut Renderer,
175 ) {
176 self.win_dims = scaled_resolution;
177 self.window_scissor = default_scissor(physical_resolution);
178
179 self.update_resolution_dependents(physical_resolution);
180
181 self.cache.resize_graphic_cache(renderer);
183 self.cache.resize_glyph_cache(renderer).unwrap();
185 }
186
187 pub fn draw(
188 &mut self,
189 primitive: Primitive,
190 renderer: &mut Renderer,
191 pool: Option<&SlowJobPool>,
192 ) {
193 span!(_guard, "draw", "IcedRenderer::draw");
194 self.draw_commands.clear();
196 self.mesh.clear();
197 self.glyphs.clear();
198
199 self.current_state = State::Plain;
200 self.start = 0;
201
202 self.draw_primitive(primitive, Vec2::zero(), 1.0, renderer, pool);
203
204 self.draw_commands.push(match self.current_state {
206 State::Plain => DrawCommand::plain(self.start..self.mesh.vertices().len()),
207 State::Image(id) => DrawCommand::image(self.start..self.mesh.vertices().len(), id),
208 });
209
210 let (glyph_cache, (cache_tex, _)) = self.cache.glyph_cache_mut_and_tex();
231 let half_res = self.half_res;
232
233 let brush_result = glyph_cache.process_queued(
234 |rect, tex_data| {
235 let offset = rect.min;
236 let size = [rect.width(), rect.height()];
237
238 let new_data = tex_data
239 .iter()
240 .map(|x| [255, 255, 255, *x])
241 .collect::<Vec<[u8; 4]>>();
242
243 renderer.update_texture(cache_tex, offset, size, &new_data);
244 },
245 |vertex_data| {
247 let uv_rect = vertex_data.tex_coords;
248 let uv = Aabr {
249 min: Vec2::new(uv_rect.min.x, uv_rect.max.y),
250 max: Vec2::new(uv_rect.max.x, uv_rect.min.y),
251 };
252 let pixel_coords = vertex_data.pixel_coords;
253 let rect = Aabr {
254 min: Vec2::new(
255 pixel_coords.min.x / half_res.x - 1.0,
256 1.0 - pixel_coords.max.y / half_res.y,
257 ),
258 max: Vec2::new(
259 pixel_coords.max.x / half_res.x - 1.0,
260 1.0 - pixel_coords.min.y / half_res.y,
261 ),
262 };
263 (uv, rect)
264 },
265 );
266
267 match brush_result {
268 Ok(brush_action) => {
269 match brush_action {
270 glyph_brush::BrushAction::Draw(verts) => self.last_glyph_verts = verts,
271 glyph_brush::BrushAction::ReDraw => {},
272 }
273
274 let glyphs = &self.glyphs;
275 let mesh = &mut self.mesh;
276 let p_scale = self.p_scale;
277 let half_res = self.half_res;
278
279 glyphs
280 .iter()
281 .flat_map(|(mesh_index, glyph_count, linear_color, offset)| {
282 let mesh_index = *mesh_index;
283 let linear_color = *linear_color;
284 let offset =
286 offset.map(|e| e as f32 * p_scale) / half_res * Vec2::new(-1.0, 1.0);
287 (0..*glyph_count).map(move |i| (mesh_index + i * 6, linear_color, offset))
288 })
289 .zip(self.last_glyph_verts.iter())
290 .for_each(|((mesh_index, linear_color, offset), (uv, rect))| {
291 let rect = Aabr {
293 min: rect.min + offset,
294 max: rect.max + offset,
295 };
296
297 mesh.replace_quad(
298 mesh_index,
299 create_ui_quad(rect, *uv, linear_color, UiMode::Text),
300 )
301 });
302 },
303 Err(glyph_brush::BrushError::TextureTooSmall { suggested: (x, y) }) => {
304 tracing::error!(
305 "Texture to small for all glyphs, would need one of the size: ({}, {})",
306 x,
307 y
308 );
309 },
310 }
311
312 if self.model.len() < self.mesh.vertices().len() {
315 self.model = renderer.create_dynamic_model(self.mesh.vertices().len() * 4 / 3);
316 }
317 renderer.update_model(&self.model, &self.mesh, 0);
319 }
320
321 fn calculate_resolution_dependents(
323 res: Vec2<u32>,
324 win_dims: Vec2<f32>,
325 ) -> (Vec2<f32>, Vec2<f32>, f32) {
326 let half_res = res.map(|e| e as f32 / 2.0);
327 let align = align(res);
328 let p_scale = res.x as f32 / win_dims.x;
330
331 (half_res, align, p_scale)
332 }
333
334 fn update_resolution_dependents(&mut self, res: Vec2<u32>) {
335 let (half_res, align, p_scale) = Self::calculate_resolution_dependents(res, self.win_dims);
336 self.half_res = half_res;
337 self.align = align;
338 self.p_scale = p_scale;
339 }
340
341 fn gl_aabr(&self, bounds: iced::Rectangle) -> Aabr<f32> {
342 let flipped_y = self.win_dims.y - bounds.y;
343 let half_win_dims = self.win_dims.map(|e| e / 2.0);
344 let half_res = self.half_res;
345 let min = (((Vec2::new(bounds.x, flipped_y - bounds.height) - half_win_dims)
346 / half_win_dims
347 * half_res
348 + self.align)
349 .map(|e| e.round())
350 - self.align)
351 / half_res;
352 let max = (((Vec2::new(bounds.x + bounds.width, flipped_y) - half_win_dims)
353 / half_win_dims
354 * half_res
355 + self.align)
356 .map(|e| e.round())
357 - self.align)
358 / half_res;
359 Aabr { min, max }
360 }
361
362 fn position_glyphs(
363 &mut self,
364 bounds: iced::Rectangle,
365 horizontal_alignment: iced::HorizontalAlignment,
366 vertical_alignment: iced::VerticalAlignment,
367 text: &str,
368 size: u16,
369 font: FontId,
370 ) -> Vec<glyph_brush::SectionGlyph> {
371 use glyph_brush::{GlyphCruncher, HorizontalAlign, VerticalAlign};
372 let (x, h_align) = match horizontal_alignment {
375 iced::HorizontalAlignment::Left => (bounds.x, HorizontalAlign::Left),
376 iced::HorizontalAlignment::Center => (bounds.center_x(), HorizontalAlign::Center),
377 iced::HorizontalAlignment::Right => (bounds.x + bounds.width, HorizontalAlign::Right),
378 };
379
380 let (y, v_align) = match vertical_alignment {
381 iced::VerticalAlignment::Top => (bounds.y, VerticalAlign::Top),
382 iced::VerticalAlignment::Center => (bounds.center_y(), VerticalAlign::Center),
383 iced::VerticalAlignment::Bottom => (bounds.y + bounds.height, VerticalAlign::Bottom),
384 };
385
386 let p_scale = self.p_scale;
387
388 let section = glyph_brush::Section {
389 screen_position: (x * p_scale, y * p_scale),
390 bounds: (bounds.width * p_scale, bounds.height * p_scale),
391 layout: glyph_brush::Layout::Wrap {
392 line_breaker: Default::default(),
393 h_align,
394 v_align,
395 },
396 text: vec![glyph_brush::Text {
397 text,
398 scale: (size as f32 * p_scale).into(),
399 font_id: font.0,
400 extra: (),
401 }],
402 };
403
404 self
405 .cache
406 .glyph_cache_mut()
407 .glyphs(section)
408 .filter(|g| {
414 !text[g.byte_index..]
415 .chars()
416 .next()
417 .unwrap()
418 .is_whitespace()
419 })
420 .cloned()
421 .collect()
422 }
423
424 fn draw_primitive(
425 &mut self,
426 primitive: Primitive,
427 offset: Vec2<u32>,
428 alpha: f32,
429 renderer: &mut Renderer,
430 pool: Option<&SlowJobPool>,
431 ) {
432 match primitive {
433 Primitive::Group { primitives } => {
434 primitives
435 .into_iter()
436 .for_each(|p| self.draw_primitive(p, offset, alpha, renderer, pool));
437 },
438 Primitive::Image {
439 handle,
440 bounds,
441 color,
442 source_rect,
443 } => {
444 let color = srgba_to_linear(color.map(|e| e as f32 / 255.0));
445 let color = apply_alpha(color, alpha);
446 if color.a == 0.0 {
448 return;
449 }
450
451 let (graphic_id, rotation) = handle;
452 let gl_aabr = self.gl_aabr(iced::Rectangle {
453 x: bounds.x - offset.x as f32,
454 y: bounds.y - offset.y as f32,
455 ..bounds
456 });
457
458 let graphic_cache = self.cache.graphic_cache_mut();
459 let half_res = self.half_res; let (source_aabr, gl_size) = {
461 let ((uv_l, uv_r, uv_b, uv_t), gl_size) = match graphic_cache
464 .get_graphic(graphic_id)
465 {
466 Some(Graphic::Blank) | None => return,
467 Some(Graphic::Image(image, ..)) => {
468 source_rect.and_then(|src_rect| {
469 #[rustfmt::skip] use ::image::GenericImageView;
470 let (image_w, image_h) = image.dimensions();
471 let (source_w, source_h) = src_rect.size().into_tuple();
472 let gl_size = gl_aabr.size();
473 if image_w == 0
474 || image_h == 0
475 || source_w < 1.0
476 || source_h < 1.0
477 || gl_size.reduce_partial_min() < f32::EPSILON
478 {
479 None
480 } else {
481 let ratio_x = (image_w as f32 / source_w)
490 .min((image_w as f32 / (gl_size.w * half_res.x)).max(1.0));
491 let ratio_y = (image_h as f32 / source_h)
492 .min((image_h as f32 / (gl_size.h * half_res.y)).max(1.0));
493 let (l, b) = src_rect.min.into_tuple();
494 let (r, t) = src_rect.max.into_tuple();
495 Some((
496 (
497 l / image_w as f32, r / image_w as f32, b / image_h as f32, t / image_h as f32, ),
502 Extent2::new(gl_size.w * ratio_x, gl_size.h * ratio_y),
503 ))
504 }
509 })
510 },
511 Some(Graphic::Voxel(..)) => None,
513 }
514 .unwrap_or_else(|| ((0.0, 1.0, 0.0, 1.0), gl_aabr.size()));
515 (
516 Aabr {
517 min: Vec2::new(uv_l, uv_b),
518 max: Vec2::new(uv_r, uv_t),
519 },
520 gl_size,
521 )
522 };
523
524 let resolution = Vec2::new(
525 (gl_size.w * self.half_res.x).round() as u16,
526 (gl_size.h * self.half_res.y).round() as u16,
527 );
528
529 if resolution.map(|e| e == 0).reduce_or() {
531 return;
532 }
534
535 let (uv_aabr, scale, tex_id) = match graphic_cache.cache_res(
537 renderer,
538 pool,
539 graphic_id,
540 resolution,
541 source_aabr.map(|e| e as f64),
543 rotation,
544 ) {
545 Some(((aabr, scale), tex_id)) => {
547 let cache_dims = graphic_cache
548 .get_tex(tex_id)
549 .0
550 .get_dimensions()
551 .xy()
552 .map(|e| e as f32);
553 let min = Vec2::new(aabr.min.x as f32, aabr.max.y as f32) / cache_dims;
554 let max = Vec2::new(aabr.max.x as f32, aabr.min.y as f32) / cache_dims;
555 (Aabr { min, max }, scale, tex_id)
556 },
557 None => return,
558 };
559
560 self.switch_state(State::Image(tex_id));
563
564 self.mesh
565 .push_quad(create_ui_quad(gl_aabr, uv_aabr, color, UiMode::Image {
566 scale,
567 }));
568 },
569 Primitive::Gradient {
570 bounds,
571 top_linear_color,
572 bottom_linear_color,
573 } => {
574 let top_linear_color = apply_alpha(top_linear_color, alpha);
576 let bottom_linear_color = apply_alpha(bottom_linear_color, alpha);
577 if top_linear_color.a == 0.0 && bottom_linear_color.a == 0.0 {
578 return;
579 }
580
581 self.switch_state(State::Plain);
582
583 let gl_aabr = self.gl_aabr(iced::Rectangle {
584 x: bounds.x - offset.x as f32,
585 y: bounds.y - offset.y as f32,
586 ..bounds
587 });
588
589 self.mesh.push_quad(create_ui_quad_vert_gradient(
590 gl_aabr,
591 Aabr {
592 min: Vec2::zero(),
593 max: Vec2::zero(),
594 },
595 top_linear_color,
596 bottom_linear_color,
597 UiMode::Geometry,
598 ));
599 },
600
601 Primitive::Rectangle {
602 bounds,
603 linear_color,
604 } => {
605 let linear_color = apply_alpha(linear_color, alpha);
606 if linear_color.a == 0.0 {
608 return;
609 }
610
611 self.switch_state(State::Plain);
612
613 let gl_aabr = self.gl_aabr(iced::Rectangle {
614 x: bounds.x - offset.x as f32,
615 y: bounds.y - offset.y as f32,
616 ..bounds
617 });
618
619 self.mesh.push_quad(create_ui_quad(
620 gl_aabr,
621 Aabr {
622 min: Vec2::zero(),
623 max: Vec2::zero(),
624 },
625 linear_color,
626 UiMode::Geometry,
627 ));
628 },
629 Primitive::Text {
630 glyphs,
631 bounds: _, linear_color,
633 } => {
634 let linear_color = apply_alpha(linear_color, alpha);
635 self.switch_state(State::Plain);
636
637 let glyph_cache = self.cache.glyph_cache_mut();
640
641 let glyph_count = glyphs.len();
643
644 glyph_cache.queue_pre_positioned(
646 glyphs,
647 vec![(); glyph_count],
649 glyph_brush::ab_glyph::Rect {
656 min: glyph_brush::ab_glyph::point(
657 -10000.0, -10000.0, ),
660 max: glyph_brush::ab_glyph::point(
661 10000.0, 10000.0, ),
664 },
665 );
666
667 let zero_aabr = Aabr {
669 min: Vec2::broadcast(0.0),
670 max: Vec2::broadcast(0.0),
671 };
672 self.glyphs.push((
673 self.mesh.vertices().len(),
674 glyph_count,
675 linear_color,
676 offset,
677 ));
678 for _ in 0..glyph_count {
679 self.mesh.push_quad(create_ui_quad(
684 zero_aabr,
685 zero_aabr,
686 linear_color,
687 UiMode::Text,
688 ));
689 }
690 },
691 Primitive::Clip {
692 bounds,
693 offset: clip_offset,
694 content,
695 } => {
696 let new_scissor = {
697 let intersection = Aabr {
699 min: Vec2 {
700 x: (bounds.x * self.p_scale) as u16,
701 y: (bounds.y * self.p_scale) as u16,
702 },
703 max: Vec2 {
704 x: ((bounds.x + bounds.width) * self.p_scale) as u16,
705 y: ((bounds.y + bounds.height) * self.p_scale) as u16,
706 },
707 }
708 .intersection(self.window_scissor);
709
710 if intersection.is_valid() && intersection.size().map(|s| s > 0).reduce_and() {
711 intersection
712 } else {
713 return;
716 }
717 };
718 self.draw_commands.push(match self.current_state {
724 State::Plain => DrawCommand::plain(self.start..self.mesh.vertices().len()),
725 State::Image(id) => {
726 DrawCommand::image(self.start..self.mesh.vertices().len(), id)
727 },
728 });
729 self.start = self.mesh.vertices().len();
730
731 self.draw_commands.push(DrawCommand::Scissor(new_scissor));
732
733 self.draw_primitive(*content, offset + clip_offset, alpha, renderer, pool);
740
741 self.draw_commands.push(match self.current_state {
743 State::Plain => DrawCommand::plain(self.start..self.mesh.vertices().len()),
744 State::Image(id) => {
745 DrawCommand::image(self.start..self.mesh.vertices().len(), id)
746 },
747 });
748 self.start = self.mesh.vertices().len();
749
750 self.draw_commands
751 .push(DrawCommand::Scissor(self.window_scissor));
752 },
753 Primitive::Opacity { alpha: a, content } => {
754 self.draw_primitive(*content, offset, alpha * a, renderer, pool);
755 },
756 Primitive::Nothing => {},
757 }
758 }
759
760 fn switch_state(&mut self, state: State) {
763 if self.current_state != state {
764 let vert_range = self.start..self.mesh.vertices().len();
765 let draw_command = match self.current_state {
766 State::Plain => DrawCommand::plain(vert_range),
767 State::Image(id) => DrawCommand::image(vert_range, id),
768 };
769 self.draw_commands.push(draw_command);
770 self.start = self.mesh.vertices().len();
771 self.current_state = state;
772 }
773 }
774
775 pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) {
776 span!(_guard, "render", "IcedRenderer::render");
777 let mut drawer = drawer.prepare(&self.interface_locals, &self.model, self.window_scissor);
778 for draw_command in self.draw_commands.iter() {
779 match draw_command {
780 DrawCommand::Scissor(new_scissor) => {
781 drawer.set_scissor(*new_scissor);
782 },
783 DrawCommand::WorldPos(index) => {
784 drawer.set_locals(
785 index.map_or(&self.interface_locals, |i| &self.ingame_locals[i]),
786 );
787 },
788 DrawCommand::Draw { kind, verts } => {
789 let tex = match kind {
791 DrawKind::Image(tex_id) => self.cache.graphic_cache().get_tex(*tex_id),
792 DrawKind::Plain => self.cache.glyph_cache_tex(),
793 };
794 drawer.draw(tex.1, verts.clone()); },
796 }
797 }
798 }
799}
800
801#[inline(always)]
804fn align(res: Vec2<u32>) -> Vec2<f32> {
805 res.map(|e| (e & 1) as f32 * 0.5)
810}
811
812fn default_scissor(physical_resolution: Vec2<u32>) -> Aabr<u16> {
813 let (screen_w, screen_h) = physical_resolution.into_tuple();
814 Aabr {
815 min: Vec2 { x: 0, y: 0 },
816 max: Vec2 {
817 x: screen_w as u16,
818 y: screen_h as u16,
819 },
820 }
821}
822
823impl iced::Renderer for IcedRenderer {
824 type Defaults = Defaults;
826 type Output = (Primitive, iced::mouse::Interaction);
828
829 fn layout<M>(
830 &mut self,
831 element: &iced::Element<'_, M, Self>,
832 limits: &iced::layout::Limits,
833 ) -> iced::layout::Node {
834 span!(_guard, "layout", "IcedRenderer::layout");
835
836 element.layout(self, limits)
839 }
840
841 fn overlay(
842 &mut self,
843 (base_primitive, base_interaction): Self::Output,
844 (overlay_primitive, overlay_interaction): Self::Output,
845 overlay_bounds: iced::Rectangle,
846 ) -> Self::Output {
847 span!(_guard, "overlay", "IcedRenderer::overlay");
848 (
849 Primitive::Group {
850 primitives: vec![base_primitive, Primitive::Clip {
851 bounds: iced::Rectangle {
852 width: overlay_bounds.width + 0.5,
854 height: overlay_bounds.height + 0.5,
855 ..overlay_bounds
856 },
857 offset: Vec2::new(0, 0),
858 content: Box::new(overlay_primitive),
859 }],
860 },
861 base_interaction.max(overlay_interaction),
862 )
863 }
864}
865
866fn apply_alpha(color: Rgba<f32>, alpha: f32) -> Rgba<f32> {
867 Rgba {
868 a: alpha * color.a,
869 ..color
870 }
871}
872