1use bytemuck::Pod;
2use fontdue::{
3 Font,
4 layout::{
5 CoordinateSystem, HorizontalAlign, Layout as TextLayout, LayoutSettings, TextStyle,
6 VerticalAlign,
7 },
8};
9use raui_core::{
10 Scalar,
11 layout::{CoordsMapping, Layout},
12 renderer::Renderer,
13 widget::{
14 WidgetId,
15 unit::{
16 WidgetUnit,
17 image::{
18 ImageBoxColor, ImageBoxImage, ImageBoxImageScaling, ImageBoxMaterial,
19 ImageBoxProceduralMesh,
20 },
21 text::{TextBoxHorizontalAlign, TextBoxVerticalAlign},
22 },
23 utils::{Color, Rect, Transform, Vec2, lerp},
24 },
25};
26use spitfire_core::{Triangle, VertexStream};
27use spitfire_fontdue::{TextRenderer, TextVertex};
28use std::collections::HashMap;
29
30#[derive(Debug, Clone)]
31pub enum Error {
32 WidgetHasNoLayout(WidgetId),
33 UnsupportedImageMaterial(Box<ImageBoxMaterial>),
34 FontNotFound(String),
35 ImageNotFound(String),
36}
37
38pub trait TesselateVertex: Pod {
39 fn apply(&mut self, position: [f32; 2], tex_coord: [f32; 3], color: [f32; 4]);
40 fn transform(&mut self, matrix: vek::Mat4<f32>);
41}
42
43#[derive(Debug, Clone, PartialEq)]
44pub enum TesselateBatch {
45 Color,
46 Image {
47 id: String,
48 },
49 Text,
50 Procedural {
51 id: String,
52 images: Vec<String>,
53 parameters: HashMap<String, Scalar>,
54 },
55 ClipPush {
56 x: f32,
57 y: f32,
58 w: f32,
59 h: f32,
60 },
61 ClipPop,
62 Debug,
63}
64
65pub trait TesselateResourceProvider {
66 fn image_id_and_uv_and_size_by_atlas_id(&self, id: &str) -> Option<(String, Rect, Vec2)>;
67 fn fonts(&self) -> &[Font];
68 fn font_index_by_id(&self, id: &str) -> Option<usize>;
69}
70
71pub trait TesselateBatchConverter<B> {
72 fn convert(&mut self, batch: TesselateBatch) -> Option<B>;
73}
74
75impl TesselateBatchConverter<TesselateBatch> for () {
76 fn convert(&mut self, batch: TesselateBatch) -> Option<TesselateBatch> {
77 Some(batch)
78 }
79}
80
81#[derive(Debug, Default, Clone, Copy)]
82pub struct TessselateRendererDebug {
83 pub render_non_visual_nodes: bool,
84}
85
86pub struct TesselateRenderer<'a, V, B, P, C>
87where
88 V: TesselateVertex + TextVertex<Color> + Default,
89 B: PartialEq,
90 P: TesselateResourceProvider,
91 C: TesselateBatchConverter<B>,
92{
93 provider: &'a P,
94 converter: &'a mut C,
95 stream: &'a mut VertexStream<V, B>,
96 text_renderer: &'a mut TextRenderer<Color>,
97 transform_stack: Vec<vek::Mat4<Scalar>>,
98 debug: Option<TessselateRendererDebug>,
99}
100
101impl<'a, V, B, P, C> TesselateRenderer<'a, V, B, P, C>
102where
103 V: TesselateVertex + TextVertex<Color> + Default,
104 B: PartialEq,
105 C: TesselateBatchConverter<B>,
106 P: TesselateResourceProvider,
107{
108 pub fn new(
109 provider: &'a P,
110 converter: &'a mut C,
111 stream: &'a mut VertexStream<V, B>,
112 text_renderer: &'a mut TextRenderer<Color>,
113 debug: Option<TessselateRendererDebug>,
114 ) -> Self {
115 Self {
116 provider,
117 converter,
118 stream,
119 text_renderer,
120 transform_stack: Default::default(),
121 debug,
122 }
123 }
124
125 fn push_transform(&mut self, transform: &Transform, rect: Rect) {
126 let size = rect.size();
127 let offset = vek::Vec2::new(rect.left, rect.top);
128 let offset = vek::Mat4::<Scalar>::translation_2d(offset);
129 let pivot = vek::Vec2::new(
130 lerp(0.0, size.x, transform.pivot.x),
131 lerp(0.0, size.y, transform.pivot.y),
132 );
133 let pivot = vek::Mat4::<Scalar>::translation_2d(pivot);
134 let inv_pivot = pivot.inverted();
135 let align = vek::Vec2::new(
136 lerp(0.0, size.x, transform.align.x),
137 lerp(0.0, size.y, transform.align.y),
138 );
139 let align = vek::Mat4::<Scalar>::translation_2d(align);
140 let translate = vek::Mat4::<Scalar>::translation_2d(raui_to_vec2(transform.translation));
141 let rotate = vek::Mat4::<Scalar>::rotation_z(transform.rotation);
142 let scale = vek::Mat4::<Scalar>::scaling_3d(raui_to_vec2(transform.scale).with_z(1.0));
143 let skew = vek::Mat4::<Scalar>::from(vek::Mat2::new(
144 1.0,
145 transform.skew.y.tan(),
146 transform.skew.x.tan(),
147 1.0,
148 ));
149 let matrix = offset * align * pivot * translate * rotate * scale * skew * inv_pivot;
150 self.push_matrix(matrix);
151 }
152
153 fn push_transform_simple(&mut self, rect: Rect) {
154 let offset = vek::Vec2::new(rect.left, rect.top);
155 let offset = vek::Mat4::<Scalar>::translation_2d(offset);
156 self.push_matrix(offset);
157 }
158
159 fn push_matrix(&mut self, matrix: vek::Mat4<Scalar>) {
160 let matrix = self.top_transform() * matrix;
161 self.transform_stack.push(matrix);
162 }
163
164 fn pop_transform(&mut self) {
165 self.transform_stack.pop();
166 }
167
168 fn top_transform(&self) -> vek::Mat4<Scalar> {
169 self.transform_stack.last().cloned().unwrap_or_default()
170 }
171
172 fn make_vertex(position: Vec2, tex_coord: Vec2, page: Scalar, color: Color) -> V {
173 let mut result = V::default();
174 TesselateVertex::apply(
175 &mut result,
176 [position.x, position.y],
177 [tex_coord.x, tex_coord.y, page],
178 [color.r, color.g, color.b, color.a],
179 );
180 result
181 }
182
183 fn make_tiled_triangle_first(offset: usize) -> Triangle {
184 Triangle { a: 0, b: 1, c: 5 }.offset(offset)
185 }
186
187 fn make_tiled_triangle_second(offset: usize) -> Triangle {
188 Triangle { a: 5, b: 4, c: 0 }.offset(offset)
189 }
190
191 fn produce_color_triangles(&mut self, size: Vec2, scale: Vec2, data: &ImageBoxColor) {
192 let matrix = self.top_transform();
193 let tl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, 0.0)));
194 let tr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, 0.0)));
195 let br = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, size.y)));
196 let bl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, size.y)));
197 let c = data.color;
198 match &data.scaling {
199 ImageBoxImageScaling::Stretch => {
200 if let Some(batch) = self.converter.convert(TesselateBatch::Color) {
201 self.stream.batch_optimized(batch);
202 self.stream.quad([
203 Self::make_vertex(tl, Default::default(), 0.0, c),
204 Self::make_vertex(tr, Default::default(), 0.0, c),
205 Self::make_vertex(br, Default::default(), 0.0, c),
206 Self::make_vertex(bl, Default::default(), 0.0, c),
207 ]);
208 }
209 }
210 ImageBoxImageScaling::Frame(frame) => {
211 let mut d = frame.destination;
212 d.left *= scale.x;
213 d.right *= scale.x;
214 d.top *= scale.y;
215 d.bottom *= scale.y;
216 if d.left + d.right > size.x {
217 let m = d.left + d.right;
218 d.left = size.x * d.left / m;
219 d.right = size.x * d.right / m;
220 }
221 if d.top + d.bottom > size.y {
222 let m = d.top + d.bottom;
223 d.top = size.y * d.top / m;
224 d.bottom = size.y * d.bottom / m;
225 }
226 let til = vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, 0.0)));
227 let tir = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x - d.right, 0.0)));
228 let itr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, d.top)));
229 let ibr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, size.y - d.bottom)));
230 let bir = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x - d.right, size.y)));
231 let bil = vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, size.y)));
232 let ibl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, size.y - d.bottom)));
233 let itl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, d.top)));
234 let itil = vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, d.top)));
235 let itir = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x - d.right, d.top)));
236 let ibir = vec2_to_raui(
237 matrix.mul_point(vek::Vec2::new(size.x - d.right, size.y - d.bottom)),
238 );
239 let ibil =
240 vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, size.y - d.bottom)));
241 if let Some(batch) = self.converter.convert(TesselateBatch::Color) {
242 self.stream.batch_optimized(batch);
243 self.stream.extend(
244 [
245 Self::make_vertex(tl, Default::default(), 0.0, c),
246 Self::make_vertex(til, Default::default(), 0.0, c),
247 Self::make_vertex(tir, Default::default(), 0.0, c),
248 Self::make_vertex(tr, Default::default(), 0.0, c),
249 Self::make_vertex(itl, Default::default(), 0.0, c),
250 Self::make_vertex(itil, Default::default(), 0.0, c),
251 Self::make_vertex(itir, Default::default(), 0.0, c),
252 Self::make_vertex(itr, Default::default(), 0.0, c),
253 Self::make_vertex(ibl, Default::default(), 0.0, c),
254 Self::make_vertex(ibil, Default::default(), 0.0, c),
255 Self::make_vertex(ibir, Default::default(), 0.0, c),
256 Self::make_vertex(ibr, Default::default(), 0.0, c),
257 Self::make_vertex(bl, Default::default(), 0.0, c),
258 Self::make_vertex(bil, Default::default(), 0.0, c),
259 Self::make_vertex(bir, Default::default(), 0.0, c),
260 Self::make_vertex(br, Default::default(), 0.0, c),
261 ],
262 [
263 Self::make_tiled_triangle_first(0),
264 Self::make_tiled_triangle_second(0),
265 Self::make_tiled_triangle_first(1),
266 Self::make_tiled_triangle_second(1),
267 Self::make_tiled_triangle_first(2),
268 Self::make_tiled_triangle_second(2),
269 Self::make_tiled_triangle_first(4),
270 Self::make_tiled_triangle_second(4),
271 Self::make_tiled_triangle_first(6),
272 Self::make_tiled_triangle_second(6),
273 Self::make_tiled_triangle_first(8),
274 Self::make_tiled_triangle_second(8),
275 Self::make_tiled_triangle_first(9),
276 Self::make_tiled_triangle_second(9),
277 Self::make_tiled_triangle_first(10),
278 Self::make_tiled_triangle_second(10),
279 ]
280 .into_iter()
281 .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_first(5)))
282 .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_second(5))),
283 );
284 }
285 }
286 }
287 }
288
289 fn produce_image_triangles(
290 &mut self,
291 id: String,
292 uvs: Rect,
293 size: Vec2,
294 rect: Rect,
295 scale: Vec2,
296 data: &ImageBoxImage,
297 ) {
298 let matrix = self.top_transform();
299 let tl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.top)));
300 let tr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.top)));
301 let br = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.bottom)));
302 let bl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.bottom)));
303 let ctl = Vec2 {
304 x: uvs.left,
305 y: uvs.top,
306 };
307 let ctr = Vec2 {
308 x: uvs.right,
309 y: uvs.top,
310 };
311 let cbr = Vec2 {
312 x: uvs.right,
313 y: uvs.bottom,
314 };
315 let cbl = Vec2 {
316 x: uvs.left,
317 y: uvs.bottom,
318 };
319 let c = data.tint;
320 match &data.scaling {
321 ImageBoxImageScaling::Stretch => {
322 if let Some(batch) = self.converter.convert(TesselateBatch::Image { id }) {
323 self.stream.batch_optimized(batch);
324 self.stream.quad([
325 Self::make_vertex(tl, ctl, 0.0, c),
326 Self::make_vertex(tr, ctr, 0.0, c),
327 Self::make_vertex(br, cbr, 0.0, c),
328 Self::make_vertex(bl, cbl, 0.0, c),
329 ]);
330 }
331 }
332 ImageBoxImageScaling::Frame(frame) => {
333 let inv_size = Vec2 {
334 x: 1.0 / size.x,
335 y: 1.0 / size.y,
336 };
337 let mut d = frame.destination;
338 d.left *= scale.x;
339 d.right *= scale.x;
340 d.top *= scale.y;
341 d.bottom *= scale.y;
342 if frame.frame_keep_aspect_ratio {
343 d.left = (frame.source.left * rect.height()) / size.y;
344 d.right = (frame.source.right * rect.height()) / size.y;
345 d.top = (frame.source.top * rect.width()) / size.x;
346 d.bottom = (frame.source.bottom * rect.width()) / size.x;
347 }
348 if d.left + d.right > rect.width() {
349 let m = d.left + d.right;
350 d.left = rect.width() * d.left / m;
351 d.right = rect.width() * d.right / m;
352 }
353 if d.top + d.bottom > rect.height() {
354 let m = d.top + d.bottom;
355 d.top = rect.height() * d.top / m;
356 d.bottom = rect.height() * d.bottom / m;
357 }
358 let til =
359 vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.top)));
360 let tir =
361 vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.top)));
362 let itr =
363 vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.top + d.top)));
364 let ibr = vec2_to_raui(
365 matrix.mul_point(vek::Vec2::new(rect.right, rect.bottom - d.bottom)),
366 );
367 let bir = vec2_to_raui(
368 matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.bottom)),
369 );
370 let bil =
371 vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.bottom)));
372 let ibl = vec2_to_raui(
373 matrix.mul_point(vek::Vec2::new(rect.left, rect.bottom - d.bottom)),
374 );
375 let itl =
376 vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.top + d.top)));
377 let itil = vec2_to_raui(
378 matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.top + d.top)),
379 );
380 let itir = vec2_to_raui(
381 matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.top + d.top)),
382 );
383 let ibir = vec2_to_raui(
384 matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.bottom - d.bottom)),
385 );
386 let ibil = vec2_to_raui(
387 matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.bottom - d.bottom)),
388 );
389 let ctil = Vec2 {
390 x: uvs.left + frame.source.left * inv_size.x,
391 y: uvs.top,
392 };
393 let ctir = Vec2 {
394 x: uvs.right - frame.source.right * inv_size.x,
395 y: uvs.top,
396 };
397 let citr = Vec2 {
398 x: uvs.right,
399 y: uvs.top + frame.source.top * inv_size.y,
400 };
401 let cibr = Vec2 {
402 x: uvs.right,
403 y: uvs.bottom - frame.source.bottom * inv_size.y,
404 };
405 let cbir = Vec2 {
406 x: uvs.right - frame.source.right * inv_size.x,
407 y: uvs.bottom,
408 };
409 let cbil = Vec2 {
410 x: uvs.left + frame.source.left * inv_size.x,
411 y: uvs.bottom,
412 };
413 let cibl = Vec2 {
414 x: uvs.left,
415 y: uvs.bottom - frame.source.bottom * inv_size.y,
416 };
417 let citl = Vec2 {
418 x: uvs.left,
419 y: uvs.top + frame.source.top * inv_size.y,
420 };
421 let citil = Vec2 {
422 x: uvs.left + frame.source.left * inv_size.x,
423 y: uvs.top + frame.source.top * inv_size.y,
424 };
425 let citir = Vec2 {
426 x: uvs.right - frame.source.right * inv_size.x,
427 y: uvs.top + frame.source.top * inv_size.y,
428 };
429 let cibir = Vec2 {
430 x: uvs.right - frame.source.right * inv_size.x,
431 y: uvs.bottom - frame.source.bottom * inv_size.y,
432 };
433 let cibil = Vec2 {
434 x: uvs.left + frame.source.left * inv_size.x,
435 y: uvs.bottom - frame.source.bottom * inv_size.y,
436 };
437 if let Some(batch) = self.converter.convert(TesselateBatch::Image { id }) {
438 self.stream.batch_optimized(batch);
439 self.stream.extend(
440 [
441 Self::make_vertex(tl, ctl, 0.0, c),
442 Self::make_vertex(til, ctil, 0.0, c),
443 Self::make_vertex(tir, ctir, 0.0, c),
444 Self::make_vertex(tr, ctr, 0.0, c),
445 Self::make_vertex(itl, citl, 0.0, c),
446 Self::make_vertex(itil, citil, 0.0, c),
447 Self::make_vertex(itir, citir, 0.0, c),
448 Self::make_vertex(itr, citr, 0.0, c),
449 Self::make_vertex(ibl, cibl, 0.0, c),
450 Self::make_vertex(ibil, cibil, 0.0, c),
451 Self::make_vertex(ibir, cibir, 0.0, c),
452 Self::make_vertex(ibr, cibr, 0.0, c),
453 Self::make_vertex(bl, cbl, 0.0, c),
454 Self::make_vertex(bil, cbil, 0.0, c),
455 Self::make_vertex(bir, cbir, 0.0, c),
456 Self::make_vertex(br, cbr, 0.0, c),
457 ],
458 [
459 Self::make_tiled_triangle_first(0),
460 Self::make_tiled_triangle_second(0),
461 Self::make_tiled_triangle_first(1),
462 Self::make_tiled_triangle_second(1),
463 Self::make_tiled_triangle_first(2),
464 Self::make_tiled_triangle_second(2),
465 Self::make_tiled_triangle_first(4),
466 Self::make_tiled_triangle_second(4),
467 Self::make_tiled_triangle_first(6),
468 Self::make_tiled_triangle_second(6),
469 Self::make_tiled_triangle_first(8),
470 Self::make_tiled_triangle_second(8),
471 Self::make_tiled_triangle_first(9),
472 Self::make_tiled_triangle_second(9),
473 Self::make_tiled_triangle_first(10),
474 Self::make_tiled_triangle_second(10),
475 ]
476 .into_iter()
477 .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_first(5)))
478 .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_second(5))),
479 );
480 }
481 }
482 }
483 }
484
485 fn produce_debug_wireframe(&mut self, size: Vec2) {
486 if let Some(batch) = self.converter.convert(TesselateBatch::Debug) {
487 let rect = Rect {
488 left: 0.0,
489 right: size.x,
490 top: 0.0,
491 bottom: size.y,
492 };
493 let matrix = self.top_transform();
494 let tl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.top)));
495 let tr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.top)));
496 let br = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.bottom)));
497 let bl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.bottom)));
498 self.stream.batch_optimized(batch);
499 self.stream.quad([
500 Self::make_vertex(tl, Default::default(), 0.0, Default::default()),
501 Self::make_vertex(tr, Default::default(), 0.0, Default::default()),
502 Self::make_vertex(br, Default::default(), 0.0, Default::default()),
503 Self::make_vertex(bl, Default::default(), 0.0, Default::default()),
504 ]);
505 }
506 }
507
508 fn render_node(
509 &mut self,
510 unit: &WidgetUnit,
511 mapping: &CoordsMapping,
512 layout: &Layout,
513 local: bool,
514 ) -> Result<(), Error> {
515 match unit {
516 WidgetUnit::None | WidgetUnit::PortalBox(_) => Ok(()),
517 WidgetUnit::AreaBox(unit) => {
518 if let Some(item) = layout.items.get(&unit.id) {
519 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
520 self.push_transform_simple(local_space);
521 self.render_node(&unit.slot, mapping, layout, true)?;
522 self.pop_transform();
523 Ok(())
524 } else {
525 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
526 }
527 }
528 WidgetUnit::ContentBox(unit) => {
529 if let Some(item) = layout.items.get(&unit.id) {
530 let mut items = unit
531 .items
532 .iter()
533 .map(|item| (item.layout.depth, item))
534 .collect::<Vec<_>>();
535 items.sort_unstable_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap());
536 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
537 self.push_transform(&unit.transform, local_space);
538 if unit.clipping {
539 let size = local_space.size();
540 let matrix = self.top_transform();
541 let tl = matrix.mul_point(vek::Vec2::new(0.0, 0.0));
542 let tr = matrix.mul_point(vek::Vec2::new(size.x, 0.0));
543 let br = matrix.mul_point(vek::Vec2::new(size.x, size.y));
544 let bl = matrix.mul_point(vek::Vec2::new(0.0, size.y));
545 let x = tl.x.min(tr.x).min(br.x).min(bl.x).round();
546 let y = tl.y.min(tr.y).min(br.y).min(bl.y).round();
547 let x2 = tl.x.max(tr.x).max(br.x).max(bl.x).round();
548 let y2 = tl.y.max(tr.y).max(br.y).max(bl.y).round();
549 let w = x2 - x;
550 let h = y2 - y;
551 if let Some(batch) =
552 self.converter
553 .convert(TesselateBatch::ClipPush { x, y, w, h })
554 {
555 self.stream.batch(batch);
556 self.stream.batch_end();
557 }
558 }
559 for (_, item) in items {
560 self.render_node(&item.slot, mapping, layout, true)?;
561 }
562 if unit.clipping
563 && let Some(batch) = self.converter.convert(TesselateBatch::ClipPop)
564 {
565 self.stream.batch(batch);
566 self.stream.batch_end();
567 }
568 self.pop_transform();
569 Ok(())
570 } else {
571 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
572 }
573 }
574 WidgetUnit::FlexBox(unit) => {
575 if let Some(item) = layout.items.get(&unit.id) {
576 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
577 self.push_transform(&unit.transform, local_space);
578 for item in &unit.items {
579 self.render_node(&item.slot, mapping, layout, true)?;
580 }
581 self.pop_transform();
582 Ok(())
583 } else {
584 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
585 }
586 }
587 WidgetUnit::GridBox(unit) => {
588 if let Some(item) = layout.items.get(&unit.id) {
589 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
590 self.push_transform(&unit.transform, local_space);
591 for item in &unit.items {
592 self.render_node(&item.slot, mapping, layout, true)?;
593 }
594 self.pop_transform();
595 Ok(())
596 } else {
597 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
598 }
599 }
600 WidgetUnit::SizeBox(unit) => {
601 if let Some(item) = layout.items.get(&unit.id) {
602 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
603 self.push_transform(&unit.transform, local_space);
604 self.render_node(&unit.slot, mapping, layout, true)?;
605 self.pop_transform();
606 Ok(())
607 } else {
608 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
609 }
610 }
611 WidgetUnit::ImageBox(unit) => match &unit.material {
612 ImageBoxMaterial::Color(color) => {
613 if let Some(item) = layout.items.get(&unit.id) {
614 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
615 self.push_transform(&unit.transform, local_space);
616 self.produce_color_triangles(local_space.size(), mapping.scale(), color);
617 self.pop_transform();
618 Ok(())
619 } else {
620 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
621 }
622 }
623 ImageBoxMaterial::Image(image) => {
624 if let Some(item) = layout.items.get(&unit.id) {
625 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
626 let rect = Rect {
627 left: 0.0,
628 right: local_space.width(),
629 top: 0.0,
630 bottom: local_space.height(),
631 };
632 let (id, uvs, size) = match self
633 .provider
634 .image_id_and_uv_and_size_by_atlas_id(&image.id)
635 {
636 Some(result) => result,
637 None => return Err(Error::ImageNotFound(image.id.to_owned())),
638 };
639 let rect = if let Some(aspect) = unit.content_keep_aspect_ratio {
640 let ox = rect.left;
641 let oy = rect.top;
642 let iw = rect.width();
643 let ih = rect.height();
644 let ra = size.x / size.y;
645 let ia = iw / ih;
646 let scale = if (ra >= ia) != aspect.outside {
647 iw / size.x
648 } else {
649 ih / size.y
650 };
651 let w = size.x * scale;
652 let h = size.y * scale;
653 let ow = lerp(0.0, iw - w, aspect.horizontal_alignment);
654 let oh = lerp(0.0, ih - h, aspect.vertical_alignment);
655 Rect {
656 left: ox + ow,
657 right: ox + ow + w,
658 top: oy + oh,
659 bottom: oy + oh + h,
660 }
661 } else {
662 rect
663 };
664 self.push_transform(&unit.transform, local_space);
665 self.produce_image_triangles(id, uvs, size, rect, mapping.scale(), image);
666 self.pop_transform();
667 Ok(())
668 } else {
669 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
670 }
671 }
672 ImageBoxMaterial::Procedural(procedural) => {
673 if let Some(item) = layout.items.get(&unit.id) {
674 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
675 self.push_transform(&unit.transform, local_space);
676 if let Some(batch) = self.converter.convert(TesselateBatch::Procedural {
677 id: procedural.id.to_owned(),
678 images: procedural.images.to_owned(),
679 parameters: procedural.parameters.to_owned(),
680 }) {
681 let image_mapping =
682 CoordsMapping::new_scaling(local_space, procedural.vertex_mapping);
683 self.stream.batch_optimized(batch);
684 match &procedural.mesh {
685 ImageBoxProceduralMesh::Owned(mesh) => {
686 self.stream.extend(
687 mesh.vertices.iter().map(|vertex| {
688 Self::make_vertex(
689 image_mapping
690 .virtual_to_real_vec2(vertex.position, false),
691 vertex.tex_coord,
692 vertex.page,
693 vertex.color,
694 )
695 }),
696 mesh.triangles.iter().map(|triangle| Triangle {
697 a: triangle[0],
698 b: triangle[1],
699 c: triangle[2],
700 }),
701 );
702 }
703 ImageBoxProceduralMesh::Shared(mesh) => {
704 self.stream.extend(
705 mesh.vertices.iter().map(|vertex| {
706 Self::make_vertex(
707 image_mapping
708 .virtual_to_real_vec2(vertex.position, false),
709 vertex.tex_coord,
710 vertex.page,
711 vertex.color,
712 )
713 }),
714 mesh.triangles.iter().map(|triangle| Triangle {
715 a: triangle[0],
716 b: triangle[1],
717 c: triangle[2],
718 }),
719 );
720 }
721 ImageBoxProceduralMesh::Generator(generator) => {
722 let mesh = (generator)(local_space, &procedural.parameters);
723 self.stream.extend(
724 mesh.vertices.into_iter().map(|vertex| {
725 Self::make_vertex(
726 image_mapping
727 .virtual_to_real_vec2(vertex.position, false),
728 vertex.tex_coord,
729 vertex.page,
730 vertex.color,
731 )
732 }),
733 mesh.triangles.into_iter().map(|triangle| Triangle {
734 a: triangle[0],
735 b: triangle[1],
736 c: triangle[2],
737 }),
738 );
739 }
740 }
741 }
742 self.pop_transform();
743 Ok(())
744 } else {
745 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
746 }
747 }
748 },
749 WidgetUnit::TextBox(unit) => {
750 let font_index = match self.provider.font_index_by_id(&unit.font.name) {
751 Some(index) => index,
752 None => return Err(Error::FontNotFound(unit.font.name.to_owned())),
753 };
754 if let Some(item) = layout.items.get(&unit.id) {
755 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
756 let size = local_space.size();
757 self.push_transform(&unit.transform, local_space);
758 let matrix = self.top_transform();
759 if let Some(batch) = self.converter.convert(TesselateBatch::Text) {
760 self.stream.batch_optimized(batch);
761 self.stream.transformed(
762 |stream| {
763 let text = TextStyle::with_user_data(
764 &unit.text,
765 unit.font.size * mapping.scalar_scale(false),
766 font_index,
767 unit.color,
768 );
769 let mut layout = TextLayout::new(CoordinateSystem::PositiveYDown);
770 layout.reset(&LayoutSettings {
771 max_width: Some(size.x),
772 max_height: Some(size.y),
773 horizontal_align: match unit.horizontal_align {
774 TextBoxHorizontalAlign::Left => HorizontalAlign::Left,
775 TextBoxHorizontalAlign::Center => HorizontalAlign::Center,
776 TextBoxHorizontalAlign::Right => HorizontalAlign::Right,
777 },
778 vertical_align: match unit.vertical_align {
779 TextBoxVerticalAlign::Top => VerticalAlign::Top,
780 TextBoxVerticalAlign::Middle => VerticalAlign::Middle,
781 TextBoxVerticalAlign::Bottom => VerticalAlign::Bottom,
782 },
783 ..Default::default()
784 });
785 layout.append(self.provider.fonts(), &text);
786 self.text_renderer.include(self.provider.fonts(), &layout);
787 self.text_renderer.render_to_stream(stream);
788 },
789 |vertex| {
790 vertex.transform(matrix);
791 },
792 );
793 }
794 self.pop_transform();
795 Ok(())
796 } else {
797 Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
798 }
799 }
800 }
801 }
802
803 fn debug_render_node(
804 &mut self,
805 unit: &WidgetUnit,
806 mapping: &CoordsMapping,
807 layout: &Layout,
808 local: bool,
809 debug: TessselateRendererDebug,
810 ) {
811 match unit {
812 WidgetUnit::AreaBox(unit) => {
813 if let Some(item) = layout.items.get(&unit.id) {
814 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
815 self.push_transform_simple(local_space);
816 self.debug_render_node(&unit.slot, mapping, layout, true, debug);
817 if debug.render_non_visual_nodes {
818 self.produce_debug_wireframe(local_space.size());
819 }
820 self.pop_transform();
821 }
822 }
823 WidgetUnit::ContentBox(unit) => {
824 if let Some(item) = layout.items.get(&unit.id) {
825 let mut items = unit
826 .items
827 .iter()
828 .map(|item| (item.layout.depth, item))
829 .collect::<Vec<_>>();
830 items.sort_unstable_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap());
831 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
832 self.push_transform(&unit.transform, local_space);
833 for (_, item) in items {
834 self.debug_render_node(&item.slot, mapping, layout, true, debug);
835 }
836 if debug.render_non_visual_nodes {
837 self.produce_debug_wireframe(local_space.size());
838 }
839 self.pop_transform();
840 }
841 }
842 WidgetUnit::FlexBox(unit) => {
843 if let Some(item) = layout.items.get(&unit.id) {
844 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
845 self.push_transform(&unit.transform, local_space);
846 for item in &unit.items {
847 self.debug_render_node(&item.slot, mapping, layout, true, debug);
848 }
849 if debug.render_non_visual_nodes {
850 self.produce_debug_wireframe(local_space.size());
851 }
852 self.pop_transform();
853 }
854 }
855 WidgetUnit::GridBox(unit) => {
856 if let Some(item) = layout.items.get(&unit.id) {
857 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
858 self.push_transform(&unit.transform, local_space);
859 for item in &unit.items {
860 self.debug_render_node(&item.slot, mapping, layout, true, debug);
861 }
862 if debug.render_non_visual_nodes {
863 self.produce_debug_wireframe(local_space.size());
864 }
865 self.pop_transform();
866 }
867 }
868 WidgetUnit::SizeBox(unit) => {
869 if let Some(item) = layout.items.get(&unit.id) {
870 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
871 self.push_transform(&unit.transform, local_space);
872 self.debug_render_node(&unit.slot, mapping, layout, true, debug);
873 if debug.render_non_visual_nodes {
874 self.produce_debug_wireframe(local_space.size());
875 }
876 self.pop_transform();
877 }
878 }
879 WidgetUnit::ImageBox(unit) => {
880 if let Some(item) = layout.items.get(&unit.id) {
881 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
882 self.push_transform(&unit.transform, local_space);
883 self.produce_debug_wireframe(local_space.size());
884 self.pop_transform();
885 }
886 }
887 WidgetUnit::TextBox(unit) => {
888 if let Some(item) = layout.items.get(&unit.id) {
889 let local_space = mapping.virtual_to_real_rect(item.local_space, local);
890 self.push_transform(&unit.transform, local_space);
891 self.produce_debug_wireframe(local_space.size());
892 self.pop_transform();
893 }
894 }
895 _ => {}
896 }
897 }
898}
899
900impl<V, B, P, C> Renderer<(), Error> for TesselateRenderer<'_, V, B, P, C>
901where
902 V: TesselateVertex + TextVertex<Color> + Default,
903 B: PartialEq,
904 C: TesselateBatchConverter<B>,
905 P: TesselateResourceProvider,
906{
907 fn render(
908 &mut self,
909 tree: &WidgetUnit,
910 mapping: &CoordsMapping,
911 layout: &Layout,
912 ) -> Result<(), Error> {
913 self.transform_stack.clear();
914 self.render_node(tree, mapping, layout, false)?;
915 self.stream.batch_end();
916 if let Some(debug) = self.debug {
917 self.transform_stack.clear();
918 self.debug_render_node(tree, mapping, layout, false, debug);
919 self.stream.batch_end();
920 }
921 Ok(())
922 }
923}
924
925fn raui_to_vec2(v: Vec2) -> vek::Vec2<Scalar> {
926 vek::Vec2::new(v.x, v.y)
927}
928
929fn vec2_to_raui(v: vek::Vec2<Scalar>) -> Vec2 {
930 Vec2 { x: v.x, y: v.y }
931}