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}