raui_app/
lib.rs

1pub mod app;
2pub(crate) mod asset_manager;
3pub(crate) mod interactions;
4
5use crate::asset_manager::AssetsManager;
6use bytemuck::{Pod, Zeroable};
7use raui_core::widget::utils::Color;
8use raui_tesselate_renderer::{TesselateBatch, TesselateBatchConverter, TesselateVertex};
9use spitfire_fontdue::TextVertex;
10use spitfire_glow::{
11    graphics::Texture,
12    prelude::{GraphicsBatch, Shader},
13    renderer::{
14        GlowBlending, GlowTextureFiltering, GlowUniformValue, GlowVertexAttrib, GlowVertexAttribs,
15    },
16};
17use vek::Rect;
18
19pub use glutin::{event, window};
20
21pub mod prelude {
22    pub use crate::{
23        app::*,
24        app::{declarative::*, immediate::*, retained::*},
25        event::*,
26        window::*,
27    };
28}
29
30#[derive(Debug, Clone, Copy, Pod, Zeroable)]
31#[repr(C)]
32pub struct Vertex {
33    pub position: [f32; 2],
34    pub uv: [f32; 3],
35    pub color: [f32; 4],
36}
37
38impl Default for Vertex {
39    fn default() -> Self {
40        Self {
41            position: Default::default(),
42            uv: Default::default(),
43            color: [1.0, 1.0, 1.0, 1.0],
44        }
45    }
46}
47
48impl GlowVertexAttribs for Vertex {
49    const ATTRIBS: &'static [(&'static str, GlowVertexAttrib)] = &[
50        (
51            "a_position",
52            GlowVertexAttrib::Float {
53                channels: 2,
54                normalized: false,
55            },
56        ),
57        (
58            "a_uv",
59            GlowVertexAttrib::Float {
60                channels: 3,
61                normalized: false,
62            },
63        ),
64        (
65            "a_color",
66            GlowVertexAttrib::Float {
67                channels: 4,
68                normalized: false,
69            },
70        ),
71    ];
72}
73
74impl TesselateVertex for Vertex {
75    fn apply(&mut self, position: [f32; 2], tex_coord: [f32; 3], color: [f32; 4]) {
76        self.position = position;
77        self.uv = tex_coord;
78        self.color = color;
79    }
80
81    fn transform(&mut self, matrix: vek::Mat4<f32>) {
82        let result = matrix.mul_point(vek::Vec3 {
83            x: self.position[0],
84            y: self.position[1],
85            z: 0.0,
86        });
87        self.position[0] = result.x;
88        self.position[1] = result.y;
89    }
90}
91
92impl TextVertex<Color> for Vertex {
93    fn apply(&mut self, position: [f32; 2], tex_coord: [f32; 3], color: Color) {
94        self.position = position;
95        self.uv = tex_coord;
96        self.color = [color.r, color.g, color.b, color.a];
97    }
98}
99
100pub(crate) struct TesselateToGraphics<'a> {
101    colored_shader: &'a Shader,
102    textured_shader: &'a Shader,
103    text_shader: &'a Shader,
104    glyphs_texture: &'a Texture,
105    missing_texture: &'a Texture,
106    assets: &'a AssetsManager,
107    clip_stack: Vec<Rect<i32, i32>>,
108    viewport_height: i32,
109    projection_view_matrix: [f32; 16],
110}
111
112impl TesselateBatchConverter<GraphicsBatch> for TesselateToGraphics<'_> {
113    fn convert(&mut self, batch: TesselateBatch) -> Option<GraphicsBatch> {
114        match batch {
115            TesselateBatch::Color => Some(GraphicsBatch {
116                shader: Some(self.colored_shader.clone()),
117                blending: GlowBlending::Alpha,
118                scissor: self.clip_stack.last().copied(),
119                ..Default::default()
120            }),
121            TesselateBatch::Image { id } => {
122                let id = AssetsManager::parse_image_id(&id).0;
123                Some(GraphicsBatch {
124                    shader: Some(self.textured_shader.clone()),
125                    textures: vec![(
126                        self.assets
127                            .textures
128                            .get(id)
129                            .map(|texture| texture.texture.clone())
130                            .unwrap_or_else(|| self.missing_texture.clone()),
131                        GlowTextureFiltering::Linear,
132                    )],
133                    blending: GlowBlending::Alpha,
134                    scissor: self.clip_stack.last().copied(),
135                    ..Default::default()
136                })
137            }
138            TesselateBatch::Text => Some(GraphicsBatch {
139                shader: Some(self.text_shader.clone()),
140                textures: vec![(self.glyphs_texture.clone(), GlowTextureFiltering::Linear)],
141                blending: GlowBlending::Alpha,
142                scissor: self.clip_stack.last().copied(),
143                ..Default::default()
144            }),
145            TesselateBatch::Procedural {
146                id,
147                images,
148                parameters,
149            } => Some(GraphicsBatch {
150                shader: self
151                    .assets
152                    .shaders
153                    .get(&id)
154                    .map(|shader| shader.shader.clone()),
155                uniforms: parameters
156                    .into_iter()
157                    .map(|(k, v)| (k.into(), GlowUniformValue::F1(v)))
158                    .chain((0..images.len()).map(|index| {
159                        (
160                            if index > 0 {
161                                format!("u_image{}", index).into()
162                            } else {
163                                "u_image".into()
164                            },
165                            GlowUniformValue::I1(index as _),
166                        )
167                    }))
168                    .chain(std::iter::once((
169                        "u_projection_view".into(),
170                        GlowUniformValue::M4(self.projection_view_matrix),
171                    )))
172                    .collect(),
173                textures: images
174                    .into_iter()
175                    .filter_map(|id| {
176                        Some((
177                            self.assets.textures.get(&id)?.texture.to_owned(),
178                            GlowTextureFiltering::Linear,
179                        ))
180                    })
181                    .collect(),
182                scissor: self.clip_stack.last().copied(),
183                ..Default::default()
184            }),
185            TesselateBatch::ClipPush { x, y, w, h } => {
186                self.clip_stack.push(vek::Rect {
187                    x: x as _,
188                    y: self.viewport_height - y as i32 - h as i32,
189                    w: w as _,
190                    h: h as _,
191                });
192                None
193            }
194            TesselateBatch::ClipPop => {
195                self.clip_stack.pop();
196                None
197            }
198        }
199    }
200}