Skip to main content

duku/renderer/
target.rs

1// Oliver Berzs
2// https://github.com/oberzs/duku
3
4use std::f32::consts::PI;
5
6use super::Light;
7use crate::color::Rgb;
8use crate::color::Rgbf;
9use crate::font::Font;
10use crate::image::Canvas;
11use crate::image::Cubemap;
12use crate::image::Filter;
13use crate::image::Texture;
14use crate::image::Wrap;
15use crate::math::Mat4;
16use crate::math::Quat;
17use crate::math::Vec2;
18use crate::math::Vec3;
19use crate::mesh::Mesh;
20use crate::mesh::Model;
21use crate::mesh::ModelNode;
22use crate::pipeline::Material;
23use crate::pipeline::Shader;
24use crate::resources::Builtins;
25use crate::resources::Handle;
26
27/// Active render target.
28///
29/// Records rendering commands and settings.
30/// Received from the [draw](crate::duku::Duku::draw) or
31/// [draw_on_canvas](crate::duku::Duku::draw_on_canvas)
32/// functions.
33pub struct Target {
34    // colors
35    fill: Rgb,
36    stroke: Rgb,
37    tint: Rgb,
38    pub(crate) background: Rgb,
39
40    // shadows
41    shadows: bool,
42    pub(crate) shadow_depth: f32,
43    pub(crate) shadow_split: f32,
44    pub(crate) shadow_softness: Pcf,
45
46    // lights
47    light_index: usize,
48    pub(crate) lights: [Light; 4],
49    pub(crate) ambient: Vec3,
50    pub(crate) exposure: f32,
51
52    // other
53    matrix: Mat4,
54    stroke_weight: f32,
55    font_size: u32,
56    shape_mode: ShapeMode,
57    border_mode: BorderMode,
58    filter: Filter,
59    wrap: Wrap,
60
61    // resources
62    shader: Option<Handle<Shader>>,
63    material: Option<Handle<Material>>,
64    font: Option<Handle<Font>>,
65    pub(crate) skybox: Option<Handle<Cubemap>>,
66    pub(crate) builtins: Builtins,
67
68    // orders
69    pub(crate) mesh_orders: Vec<ShaderOrder>,
70    pub(crate) line_orders: Vec<LineOrder>,
71    pub(crate) tri_orders: Vec<TriOrder>,
72    pub(crate) char_orders: Vec<CharOrder>,
73
74    cache: Vec<Cache>,
75}
76
77/// Shape positioning mode.
78#[derive(Debug, Copy, Clone, PartialEq, Eq)]
79pub enum ShapeMode {
80    /// position from bottom-left corner
81    BottomLeft,
82    /// position from bottom-right corner
83    BottomRight,
84    /// position from top-left corner
85    TopLeft,
86    /// position from top-right corner
87    TopRight,
88    /// position from center
89    Center,
90}
91
92/// Border positioning mode.
93#[derive(Debug, Copy, Clone, PartialEq, Eq)]
94pub enum BorderMode {
95    /// put border on the outside of shape
96    Outside,
97    /// put border on the inside of shape
98    Inside,
99    /// pub border evenly on the inside
100    /// and outside of shape
101    Center,
102}
103
104/// Shadow softening used when sampling.
105#[derive(Debug, Copy, Clone, PartialEq, Eq)]
106pub enum Pcf {
107    /// sample shadow map 16 times
108    X16,
109    /// sample shadow map 4 times
110    X4,
111    /// sample shadow map 1 time
112    Disabled,
113}
114
115pub(crate) struct ShaderOrder {
116    pub(crate) shader: Handle<Shader>,
117    pub(crate) orders: Vec<MaterialOrder>,
118}
119
120pub(crate) struct MaterialOrder {
121    pub(crate) material: Handle<Material>,
122    pub(crate) orders: Vec<MeshOrder>,
123}
124
125pub(crate) struct MeshOrder {
126    pub(crate) mesh: Handle<Mesh>,
127    pub(crate) matrix: Mat4,
128    pub(crate) color: Rgb,
129    pub(crate) shadows: bool,
130    pub(crate) sampler_index: u32,
131}
132
133pub(crate) struct CharOrder {
134    pub(crate) points: [Vec3; 4],
135    pub(crate) uvs: [Vec2; 4],
136    pub(crate) color: Rgb,
137    pub(crate) texture: u32,
138}
139
140pub(crate) struct LineOrder {
141    pub(crate) points: [Vec3; 2],
142    pub(crate) color: Rgb,
143}
144
145pub(crate) struct TriOrder {
146    pub(crate) points: [Vec3; 3],
147    pub(crate) uvs: [Vec2; 3],
148    pub(crate) color: Rgb,
149    pub(crate) texture: u32,
150    pub(crate) sampler_index: u32,
151    pub(crate) opaque: bool,
152}
153
154struct Cache {
155    shader: Option<Handle<Shader>>,
156    material: Option<Handle<Material>>,
157    font: Option<Handle<Font>>,
158
159    // colors
160    background: Rgb,
161    fill: Rgb,
162    stroke: Rgb,
163    tint: Rgb,
164
165    // shadows
166    shadows: bool,
167
168    // other
169    matrix: Mat4,
170    stroke_weight: f32,
171    font_size: u32,
172    shape_mode: ShapeMode,
173    border_mode: BorderMode,
174    filter: Filter,
175    wrap: Wrap,
176}
177
178impl Target {
179    pub(crate) fn new(builtins: &Builtins) -> Self {
180        Self {
181            background: Rgb::gray(255),
182            fill: Rgb::gray(255),
183            stroke: Rgb::gray(0),
184            tint: Rgb::gray(255),
185
186            shadows: false,
187            shadow_depth: 50.0,
188            shadow_split: 0.5,
189            shadow_softness: Pcf::X16,
190
191            lights: [Light::none(); 4],
192            light_index: 0,
193            ambient: Vec3::uniform(0.03),
194            exposure: 1.0,
195
196            matrix: Mat4::identity(),
197            stroke_weight: 2.0,
198            font_size: 24,
199            shape_mode: ShapeMode::Center,
200            border_mode: BorderMode::Center,
201            filter: Filter::Linear,
202            wrap: Wrap::Repeat,
203
204            font: None,
205            shader: None,
206            material: None,
207            skybox: None,
208            builtins: builtins.clone(),
209
210            mesh_orders: vec![],
211            char_orders: vec![],
212            line_orders: vec![],
213            tri_orders: vec![],
214
215            cache: vec![],
216        }
217    }
218
219    /// Set background color of canvas
220    pub fn background(&mut self, color: impl Into<Rgb>) {
221        self.background = color.into();
222    }
223
224    /// Set fill color for shapes and text
225    pub fn fill(&mut self, color: impl Into<Rgb>) {
226        self.fill = color.into();
227    }
228
229    /// Set stroke color for borders and lines
230    pub fn stroke(&mut self, color: impl Into<Rgb>) {
231        self.stroke = color.into();
232    }
233
234    /// Set tint color for meshes and textures
235    pub fn tint(&mut self, color: impl Into<Rgb>) {
236        self.tint = color.into();
237    }
238
239    /// Set material for meshes
240    pub fn material(&mut self, m: &Handle<Material>) {
241        self.material = Some(m.clone());
242    }
243
244    /// Use default material for meshes
245    pub fn no_material(&mut self) {
246        self.material = None;
247    }
248
249    /// Set shader for meshes
250    pub fn shader(&mut self, s: &Handle<Shader>) {
251        self.shader = Some(s.clone());
252    }
253
254    /// Use default shader for meshes
255    pub fn no_shader(&mut self) {
256        self.shader = None;
257    }
258
259    /// Set skybox for rendering
260    pub fn skybox(&mut self, s: &Handle<Cubemap>) {
261        self.skybox = Some(s.clone());
262    }
263
264    /// Set font for text
265    pub fn font(&mut self, f: &Handle<Font>) {
266        self.font = Some(f.clone());
267    }
268
269    /// Use default font for text
270    pub fn no_font(&mut self) {
271        self.font = None;
272    }
273
274    /// Enable casting shadows for meshes
275    ///
276    /// Uses the first directional light for
277    /// casting the shadows.
278    pub fn shadows(&mut self) {
279        self.shadows = true;
280    }
281
282    /// Disable casting shadows for meshes
283    pub fn no_shadows(&mut self) {
284        self.shadows = false;
285    }
286
287    /// Set max shadow depth
288    pub fn shadow_depth(&mut self, d: f32) {
289        self.shadow_depth = d;
290    }
291
292    /// Set shadow split coefficient
293    ///
294    /// 0 to 1, higher values make closer shadows
295    /// look better.
296    pub fn shadow_split(&mut self, s: f32) {
297        self.shadow_split = s;
298    }
299
300    /// Set how soft shadows appear
301    pub fn shadow_softness(&mut self, pcf: Pcf) {
302        self.shadow_softness = pcf;
303    }
304
305    /// Add light to scene
306    ///
307    /// Max is 4. When lights are full,
308    /// removes first lights.
309    pub fn light(&mut self, l: Light) {
310        self.lights[self.light_index] = l;
311        self.light_index = (self.light_index + 1) % 4;
312    }
313
314    /// Set the ambient light that affects the entire scene
315    pub fn ambient(&mut self, color: impl Into<Rgbf>, brightness: f32) {
316        self.ambient = Vec3::from(color.into()) * brightness;
317    }
318
319    /// Set the exposure for tone mapping
320    pub fn exposure(&mut self, e: f32) {
321        self.exposure = e;
322    }
323
324    /// Set the stroke weight for lines and borders
325    pub fn stroke_weight(&mut self, w: f32) {
326        self.stroke_weight = w;
327    }
328
329    /// Set font size for text
330    pub fn font_size(&mut self, s: u32) {
331        self.font_size = s;
332    }
333
334    /// Set border mode
335    pub fn border_mode(&mut self, mode: BorderMode) {
336        self.border_mode = mode;
337    }
338
339    /// Set shape mode
340    pub fn shape_mode(&mut self, mode: ShapeMode) {
341        self.shape_mode = mode;
342    }
343
344    /// Set filter for textures
345    pub fn filter(&mut self, f: Filter) {
346        self.filter = f;
347    }
348
349    /// Set wrap mode for textures
350    pub fn wrap(&mut self, w: Wrap) {
351        self.wrap = w;
352    }
353
354    /// Transform points by matrix
355    pub fn transform(&mut self, matrix: impl Into<Mat4>) {
356        self.matrix = matrix.into() * self.matrix;
357    }
358
359    /// Reset transform to default
360    pub fn reset_transform(&mut self) {
361        self.matrix = Mat4::identity();
362    }
363
364    /// Move transform by vector
365    pub fn translate(&mut self, v: impl Into<Vec3>) {
366        self.matrix = Mat4::translation(v) * self.matrix;
367    }
368
369    /// Move transform on X axis
370    pub fn translate_x(&mut self, x: f32) {
371        self.matrix = Mat4::translation(Vec3::right() * x) * self.matrix;
372    }
373
374    /// Move transform on Y axis
375    pub fn translate_y(&mut self, y: f32) {
376        self.matrix = Mat4::translation(Vec3::up() * y) * self.matrix;
377    }
378
379    /// Move transform on Z axis
380    pub fn translate_z(&mut self, z: f32) {
381        self.matrix = Mat4::translation(Vec3::forward() * z) * self.matrix;
382    }
383
384    /// Scale transform by vector
385    pub fn scale(&mut self, v: impl Into<Vec3>) {
386        self.matrix = Mat4::scale(v) * self.matrix;
387    }
388
389    /// Rotate transform by quaternion
390    pub fn rotate(&mut self, q: impl Into<Quat>) {
391        self.matrix = Mat4::from(q.into()) * self.matrix;
392    }
393
394    /// Rotate transform on the X axis
395    ///
396    /// This angle is in degrees.
397    pub fn rotate_x(&mut self, d: f32) {
398        self.matrix = Mat4::euler_rotation(d, 0.0, 0.0) * self.matrix;
399    }
400
401    /// Rotate transform on the Y axis
402    ///
403    /// This angle is in degrees.
404    pub fn rotate_y(&mut self, d: f32) {
405        self.matrix = Mat4::euler_rotation(0.0, d, 0.0) * self.matrix;
406    }
407
408    /// Rotate transform on the Z axis
409    ///
410    /// This angle is in degrees.
411    pub fn rotate_z(&mut self, d: f32) {
412        self.matrix = Mat4::euler_rotation(0.0, 0.0, d) * self.matrix;
413    }
414
415    /// Draw a 2D line
416    pub fn line(&mut self, p1: impl Into<Vec2>, p2: impl Into<Vec2>) {
417        let weight = self.stroke_weight / 2.0;
418        self.path(&[p1.into(), p2.into()], false, weight, weight);
419    }
420
421    /// Draw mitered 2D lines
422    pub fn lines(&mut self, points: &[Vec2]) {
423        let weight = self.stroke_weight / 2.0;
424        self.path(points, false, weight, weight);
425    }
426
427    /// Draw a custom 3D mesh
428    pub fn mesh(&mut self, mesh: &Handle<Mesh>) {
429        let unshaded = self.lights.iter().all(|l| l.is_none());
430
431        let order = MeshOrder {
432            mesh: mesh.clone(),
433            matrix: self.matrix,
434            color: self.tint,
435            shadows: self.shadows,
436            sampler_index: self.sampler_index(),
437        };
438
439        let shader = match &self.shader {
440            Some(s) => s,
441            None if unshaded => &self.builtins.unshaded_shader,
442            None => &self.builtins.pbr_shader,
443        };
444
445        let material = match &self.material {
446            Some(m) => m,
447            None => &self.builtins.white_material,
448        };
449
450        match self.mesh_orders.iter_mut().find(|so| &so.shader == shader) {
451            Some(so) => match so.orders.iter_mut().find(|mo| &mo.material == material) {
452                Some(mo) => mo.orders.push(order),
453                None => so.orders.push(MaterialOrder {
454                    material: material.clone(),
455                    orders: vec![order],
456                }),
457            },
458            None => self.mesh_orders.push(ShaderOrder {
459                shader: shader.clone(),
460                orders: vec![MaterialOrder {
461                    material: material.clone(),
462                    orders: vec![order],
463                }],
464            }),
465        }
466    }
467
468    /// Draw a wireframe for a 3D mesh
469    pub fn wireframe(&mut self, mesh: &Handle<Mesh>) {
470        self.push();
471        self.shadows = false;
472        self.shader = Some(self.builtins.wireframe_shader.clone());
473        self.mesh(mesh);
474        self.pop();
475    }
476
477    /// Draw a scaled cube
478    pub fn cube(&mut self, scale: impl Into<Vec3>) {
479        self.push();
480        self.matrix *= Mat4::scale(scale);
481        let mesh = self.builtins.cube_mesh.clone();
482        self.mesh(&mesh);
483        self.pop();
484    }
485
486    /// Draw a scaled uv sphere
487    pub fn sphere_uv(&mut self, scale: impl Into<Vec3>) {
488        self.push();
489        self.matrix *= Mat4::scale(scale);
490        let mesh = self.builtins.uv_sphere_mesh.clone();
491        self.mesh(&mesh);
492        self.pop();
493    }
494
495    /// Draw a scaled ico sphere
496    pub fn sphere_ico(&mut self, scale: impl Into<Vec3>) {
497        self.push();
498        self.matrix *= Mat4::scale(scale);
499        let mesh = self.builtins.ico_sphere_mesh.clone();
500        self.mesh(&mesh);
501        self.pop();
502    }
503
504    /// Draw a scaled plane
505    pub fn plane(&mut self, scale: impl Into<Vec2>) {
506        self.push();
507        self.matrix *= Mat4::scale(Vec3::from((scale.into(), 1.0)));
508        let mesh = self.builtins.plane_mesh.clone();
509        self.mesh(&mesh);
510        self.pop();
511    }
512
513    /// Draw a surface with a custom shader
514    pub fn surface(&mut self, shader: &Handle<Shader>) {
515        self.push();
516        self.shader = Some(shader.clone());
517        let mesh = self.builtins.surface_mesh.clone();
518        self.mesh(&mesh);
519        self.pop();
520    }
521
522    /// Draw a fullscreen canvas
523    pub fn fullscreen(&mut self, canvas: &Handle<Canvas>) {
524        self.push();
525        self.shader = Some(self.builtins.fullscreen_shader.clone());
526        self.material = Some(canvas.read().material().clone());
527        let mesh = self.builtins.surface_mesh.clone();
528        self.mesh(&mesh);
529        self.pop();
530    }
531
532    /// Draw all of the meshes of a model
533    pub fn model(&mut self, model: &Handle<Model>) {
534        for node in &model.read().nodes {
535            self.push();
536            self.model_node(node, self.matrix);
537            self.pop();
538        }
539    }
540
541    /// Draw a 3D debug line that isn't controlled by
542    /// [stroke_weight](crate::renderer::Target::stroke_weight)
543    pub fn debug_line<V: Into<Vec3>>(&mut self, p1: V, p2: V) {
544        self.line_orders.push(LineOrder {
545            points: [self.matrix * p1.into(), self.matrix * p2.into()],
546            color: self.stroke,
547        });
548    }
549
550    /// Draw 3D guide grid for X and Z axis
551    pub fn debug_grid(&mut self) {
552        let size = 100;
553        let half = size / 2;
554        let width = 1.0;
555
556        self.push();
557        for x in -half..half {
558            let xx = x as f32 * width;
559            let z_min = -half as f32 * width;
560            let z_max = half as f32 * width;
561
562            let color = match x {
563                0 => "#0000fff0",
564                _ => "#85858585",
565            };
566
567            self.stroke(color);
568            self.debug_line([xx, 0.0, z_min], [xx, 0.0, z_max]);
569        }
570        for z in -half..half {
571            let zz = z as f32 * width;
572            let x_min = -half as f32 * width;
573            let x_max = half as f32 * width;
574
575            let color = match z {
576                0 => "#ff0000f0",
577                _ => "#85858585",
578            };
579
580            self.stroke(color);
581            self.debug_line([x_min, 0.0, zz], [x_max, 0.0, zz]);
582        }
583        self.pop();
584    }
585
586    /// Draw text string
587    pub fn text(&mut self, text: impl AsRef<str>, pos: impl Into<Vec2>) {
588        let mut position = pos.into();
589        let t = text.as_ref();
590        let font = self
591            .font
592            .as_ref()
593            .unwrap_or(&self.builtins.fira_font)
594            .read();
595
596        let w = self.text_width(t);
597        let h = self.text_height(t);
598
599        position.y += h - self.text_height("a");
600
601        position += match self.shape_mode {
602            ShapeMode::BottomLeft => Vec2::new(0.0, 0.0),
603            ShapeMode::BottomRight => Vec2::new(-w, 0.0),
604            ShapeMode::TopLeft => Vec2::new(0.0, -h),
605            ShapeMode::TopRight => Vec2::new(-w, -h),
606            ShapeMode::Center => Vec2::new(-w / 2.0, -h / 2.0),
607        };
608
609        let metrics = font.metrics().scaled(self.font_size);
610        let start_x = position.x;
611
612        for c in t.chars() {
613            // handle whitespace
614            if c == ' ' {
615                position.x += metrics.space_width;
616                continue;
617            }
618            if c == '\n' {
619                position.x = start_x;
620                position.y -= metrics.height + metrics.line_gap;
621                continue;
622            }
623
624            // calculate positions
625            let data = font.char_data(c).scaled(self.font_size);
626            let bx = data.bounds.x;
627            let by = data.bounds.y;
628            let mut cp = position;
629            cp.x += data.bearing.x;
630            cp.y -= metrics.descender;
631            cp.y -= data.bearing.y;
632
633            // calculate points
634            let p1 = self.matrix * Vec3::new(cp.x, cp.y + by, 0.0);
635            let p2 = self.matrix * Vec3::new(cp.x + bx, cp.y + by, 0.0);
636            let p3 = self.matrix * Vec3::new(cp.x + bx, cp.y, 0.0);
637            let p4 = self.matrix * Vec3::new(cp.x, cp.y, 0.0);
638
639            let uv1 = Vec2::new(data.uvs.x, data.uvs.y);
640            let uv2 = Vec2::new(data.uvs.z, data.uvs.y);
641            let uv3 = Vec2::new(data.uvs.z, data.uvs.w);
642            let uv4 = Vec2::new(data.uvs.x, data.uvs.w);
643
644            // add order
645            self.char_orders.push(CharOrder {
646                points: [p1, p2, p3, p4],
647                uvs: [uv1, uv2, uv3, uv4],
648                color: self.stroke,
649                texture: font.texture().shader_index(),
650            });
651
652            position.x += data.advance;
653        }
654    }
655
656    /// Draw a custom shape from points
657    ///
658    /// Shape must be convex
659    pub fn shape(&mut self, points: &[Vec2]) {
660        // don't draw shape with less than 2 points
661        if points.len() < 3 {
662            return;
663        }
664
665        // check if should draw shape
666        if self.fill.a > 0 {
667            let texture = self.builtins.white_texture.read().shader_index();
668            let opaque = self.fill.a == 255;
669
670            // triangulate points
671            let first = Vec3::from((points[0], 0.0));
672            for i in 2..points.len() {
673                self.tri_orders.push(TriOrder {
674                    points: [
675                        self.matrix * first,
676                        self.matrix * Vec3::from((points[i - 1], 0.0)),
677                        self.matrix * Vec3::from((points[i], 0.0)),
678                    ],
679                    uvs: [Vec2::default(); 3],
680                    color: self.fill,
681                    sampler_index: 0,
682                    texture,
683                    opaque,
684                });
685            }
686        }
687
688        // check if should draw borders
689        if self.stroke.a > 0 {
690            let outer_weight = match self.border_mode {
691                BorderMode::Center => self.stroke_weight / 2.0,
692                BorderMode::Outside => self.stroke_weight,
693                BorderMode::Inside => 0.0,
694            };
695            let inner_weight = match self.border_mode {
696                BorderMode::Center => self.stroke_weight / 2.0,
697                BorderMode::Outside => 0.0,
698                BorderMode::Inside => self.stroke_weight,
699            };
700
701            self.path(points, true, inner_weight, outer_weight);
702        }
703    }
704
705    /// Draw a rectangle
706    pub fn rect(&mut self, pos: impl Into<Vec2>, size: impl Into<Vec2>) {
707        let s = size.into();
708        let p = pos.into();
709
710        let offset = match self.shape_mode {
711            ShapeMode::BottomLeft => Vec2::new(0.0, 0.0),
712            ShapeMode::BottomRight => Vec2::new(-s.x, 0.0),
713            ShapeMode::TopLeft => Vec2::new(0.0, -s.y),
714            ShapeMode::TopRight => Vec2::new(-s.x, -s.y),
715            ShapeMode::Center => Vec2::new(-s.x / 2.0, -s.y / 2.0),
716        };
717
718        self.shape(&[
719            Vec2::new(p.x, p.y + s.y) + offset,
720            Vec2::new(p.x + s.x, p.y + s.y) + offset,
721            Vec2::new(p.x + s.x, p.y) + offset,
722            Vec2::new(p.x, p.y) + offset,
723        ]);
724    }
725
726    /// Draw a square
727    pub fn square(&mut self, pos: impl Into<Vec2>, size: f32) {
728        self.rect(pos, Vec2::new(size, size));
729    }
730
731    /// Draw an ellipse
732    pub fn ellipse(&mut self, pos: impl Into<Vec2>, size: impl Into<Vec2>) {
733        let s = size.into() / 2.0;
734        let side_count = (s.length() * 3.0) as u32;
735        let position = pos.into();
736
737        let offset = match self.shape_mode {
738            ShapeMode::BottomLeft => s,
739            ShapeMode::BottomRight => Vec2::new(-s.x, s.y),
740            ShapeMode::TopLeft => Vec2::new(s.x, -s.y),
741            ShapeMode::TopRight => Vec2::new(-s.x, -s.y),
742            ShapeMode::Center => Vec2::new(0.0, 0.0),
743        };
744
745        let points: Vec<_> = (0..side_count)
746            .map(|i| {
747                let q = 2.0 * PI * (i as f32 / side_count as f32);
748                let x = s.x * q.cos();
749                let y = s.y * q.sin();
750                position + offset + Vec2::new(x, y)
751            })
752            .collect();
753        self.shape(&points);
754    }
755
756    /// Draw a circle
757    pub fn circle(&mut self, pos: impl Into<Vec2>, size: f32) {
758        self.ellipse(pos, Vec2::new(size, size));
759    }
760
761    /// Draw a textured quad
762    pub fn texture(
763        &mut self,
764        texture: &Handle<Texture>,
765        pos: impl Into<Vec2>,
766        size: impl Into<Vec2>,
767    ) {
768        let (tw, th) = {
769            let tex = texture.read();
770            (tex.width() as f32, tex.height() as f32)
771        };
772        self.texture_part(texture, pos, size, [0.0, 0.0], [tw, th]);
773    }
774
775    /// Draw a quad with part of a texture
776    ///
777    /// Part is defined in pixels
778    pub fn texture_part(
779        &mut self,
780        texture: &Handle<Texture>,
781        pos: impl Into<Vec2>,
782        size: impl Into<Vec2>,
783        part_pos: impl Into<Vec2>,
784        part_size: impl Into<Vec2>,
785    ) {
786        let s = size.into();
787        let p = pos.into();
788        let pp = part_pos.into();
789        let ps = part_size.into();
790        let tex = texture.read();
791        let tw = tex.width() as f32;
792        let th = tex.height() as f32;
793        let opaque = tex.opaque() && self.tint.a == 255;
794
795        let offset = match self.shape_mode {
796            ShapeMode::BottomLeft => Vec3::new(0.0, 0.0, 0.0),
797            ShapeMode::BottomRight => Vec3::new(-s.x, 0.0, 0.0),
798            ShapeMode::TopLeft => Vec3::new(0.0, -s.y, 0.0),
799            ShapeMode::TopRight => Vec3::new(-s.x, -s.y, 0.0),
800            ShapeMode::Center => Vec3::new(-s.x / 2.0, -s.y / 2.0, 0.0),
801        };
802
803        let p1 = self.matrix * (Vec3::new(p.x, p.y + s.y, 0.0) + offset);
804        let p2 = self.matrix * (Vec3::new(p.x + s.x, p.y + s.y, 0.0) + offset);
805        let p3 = self.matrix * (Vec3::new(p.x + s.x, p.y, 0.0) + offset);
806        let p4 = self.matrix * (Vec3::new(p.x, p.y, 0.0) + offset);
807
808        let uv1 = Vec2::new(pp.x / tw, pp.y / th);
809        let uv2 = Vec2::new((pp.x + ps.x) / tw, pp.y / th);
810        let uv3 = Vec2::new((pp.x + ps.x) / tw, (pp.y + ps.y) / th);
811        let uv4 = Vec2::new(pp.x / tw, (pp.y + ps.y) / th);
812
813        self.tri_orders.push(TriOrder {
814            points: [p1, p2, p3],
815            color: self.tint,
816            uvs: [uv1, uv2, uv3],
817            texture: tex.shader_index(),
818            sampler_index: self.sampler_index(),
819            opaque,
820        });
821        self.tri_orders.push(TriOrder {
822            points: [p1, p3, p4],
823            color: self.tint,
824            uvs: [uv1, uv3, uv4],
825            texture: tex.shader_index(),
826            sampler_index: self.sampler_index(),
827            opaque,
828        });
829    }
830
831    /// Get text width for current font
832    pub fn text_width(&self, text: impl AsRef<str>) -> f32 {
833        let font = self
834            .font
835            .as_ref()
836            .unwrap_or(&self.builtins.fira_font)
837            .read();
838        let metrics = font.metrics().scaled(self.font_size);
839        let mut max_width = 0.0;
840
841        // get the width of the longest line
842        for line in text.as_ref().lines() {
843            let mut width = 0.0;
844            for c in line.chars() {
845                // handle whitespace
846                if c == ' ' {
847                    width += metrics.space_width;
848                    continue;
849                }
850                let data = font.char_data(c).scaled(self.font_size);
851                width += data.advance;
852            }
853            if width > max_width {
854                max_width = width;
855            }
856        }
857
858        max_width
859    }
860
861    /// Get text height for current font
862    pub fn text_height(&self, text: impl AsRef<str>) -> f32 {
863        let font = self
864            .font
865            .as_ref()
866            .unwrap_or(&self.builtins.fira_font)
867            .read();
868        let metrics = font.metrics().scaled(self.font_size);
869        let lines = text.as_ref().lines().count();
870        (metrics.height + metrics.line_gap) * lines as f32
871    }
872
873    /// Save target settings to stack
874    pub fn push(&mut self) {
875        self.cache.push(Cache {
876            shader: self.shader.clone(),
877            material: self.material.clone(),
878            font: self.font.clone(),
879
880            background: self.background,
881            fill: self.fill,
882            stroke: self.stroke,
883            tint: self.tint,
884
885            shadows: self.shadows,
886
887            matrix: self.matrix,
888            stroke_weight: self.stroke_weight,
889            font_size: self.font_size,
890            shape_mode: self.shape_mode,
891            border_mode: self.border_mode,
892            filter: self.filter,
893            wrap: self.wrap,
894        });
895    }
896
897    /// Restore target settings from stack
898    pub fn pop(&mut self) {
899        if let Some(cache) = self.cache.pop() {
900            self.shader = cache.shader;
901            self.material = cache.material;
902            self.font = cache.font;
903
904            self.background = cache.background;
905            self.fill = cache.fill;
906            self.stroke = cache.stroke;
907            self.tint = cache.tint;
908
909            self.shadows = cache.shadows;
910
911            self.matrix = cache.matrix;
912            self.stroke_weight = cache.stroke_weight;
913            self.font_size = cache.font_size;
914            self.shape_mode = cache.shape_mode;
915            self.border_mode = cache.border_mode;
916            self.filter = cache.filter;
917            self.wrap = cache.wrap;
918        }
919    }
920
921    fn model_node(&mut self, node: &ModelNode, parent: Mat4) {
922        self.matrix = parent * node.matrix;
923        // self.matrix = node.matrix * parent;
924
925        for (mesh, material) in node.orders() {
926            self.material(material);
927            self.mesh(mesh);
928        }
929
930        for child in &node.children {
931            self.push();
932            self.model_node(child, self.matrix);
933            self.pop();
934        }
935    }
936
937    fn path(&mut self, path: &[Vec2], closed: bool, inner_weight: f32, outer_weight: f32) {
938        // generate normals
939        let mut normals = vec![];
940
941        let mut points = path.to_vec();
942        if closed {
943            points.push(path[0]);
944        }
945
946        for i in 1..points.len() {
947            let prev = points[i - 1];
948            let curr = points[i];
949            let next = points.get(i + 1);
950
951            let line_a = (curr - prev).unit();
952
953            if i == 1 {
954                // first segment
955                normals.push(line_a.normal());
956            }
957
958            if let Some(n) = next {
959                let line_b = (*n - curr).unit();
960                normals.push(miter(line_a, line_b));
961            } else {
962                // last segment
963                normals.push(line_a.normal());
964            }
965        }
966
967        if points.len() > 2 && closed {
968            // connect first and last normals
969            let prev = points[points.len() - 2];
970            let curr = points[0];
971            let next = points[1];
972
973            let line_a = (curr - prev).unit();
974            let line_b = (next - curr).unit();
975
976            let m = miter(line_a, line_b);
977            normals[0] = m;
978            normals[points.len() - 1] = m;
979        }
980
981        // draw tris
982        for i in 0..(normals.len() - 1) {
983            let curr_norm = normals[i];
984            let next_norm = normals[i + 1];
985            let curr_point = points[i];
986            let next_point = *points.get(i + 1).unwrap_or(&points[0]);
987
988            let p1 = self.matrix * Vec3::from((curr_point + curr_norm * outer_weight, -0.00001));
989            let p2 = self.matrix * Vec3::from((next_point + next_norm * outer_weight, -0.00001));
990            let p3 = self.matrix * Vec3::from((next_point - next_norm * inner_weight, -0.00001));
991            let p4 = self.matrix * Vec3::from((curr_point - curr_norm * inner_weight, -0.00001));
992
993            let texture = self.builtins.white_texture.read().shader_index();
994
995            self.tri_orders.push(TriOrder {
996                points: [p1, p2, p3],
997                color: self.stroke,
998                uvs: [Vec2::default(); 3],
999                opaque: self.stroke.a == 255,
1000                sampler_index: 0,
1001                texture,
1002            });
1003            self.tri_orders.push(TriOrder {
1004                points: [p1, p3, p4],
1005                color: self.stroke,
1006                uvs: [Vec2::default(); 3],
1007                opaque: self.stroke.a == 255,
1008                sampler_index: 0,
1009                texture,
1010            });
1011        }
1012    }
1013
1014    const fn sampler_index(&self) -> u32 {
1015        match (self.filter, self.wrap) {
1016            (Filter::Linear, Wrap::Repeat) => 0,
1017            (Filter::Linear, Wrap::ClampBorder) => 1,
1018            (Filter::Linear, Wrap::ClampEdge) => 2,
1019            (Filter::Nearest, Wrap::Repeat) => 3,
1020            (Filter::Nearest, Wrap::ClampBorder) => 4,
1021            (Filter::Nearest, Wrap::ClampEdge) => 5,
1022        }
1023    }
1024}
1025
1026fn miter(line_a: Vec2, line_b: Vec2) -> Vec2 {
1027    let tangent = (line_a + line_b).unit();
1028    let miter = tangent.normal();
1029    let norm_a = line_a.normal();
1030    let miter_len = 1.0 / miter.dot(norm_a);
1031
1032    miter * miter_len
1033}