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