raui_app/
lib.rs

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