spitfire_draw/
context.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use crate::utils::{FontMap, ResourceRef, ShaderRef, TextureRef, Vertex};
use spitfire_fontdue::TextRenderer;
use spitfire_glow::{
    graphics::{Graphics, Shader, Texture},
    renderer::{GlowBlending, GlowTextureFormat},
};
use std::{borrow::Cow, collections::HashMap};
use vek::{Rgba, Transform};

#[derive(Default, Clone)]
pub struct DrawContext {
    pub shaders: HashMap<Cow<'static, str>, Shader>,
    pub textures: HashMap<Cow<'static, str>, Texture>,
    pub fonts: FontMap,
    pub text_renderer: TextRenderer<Rgba<f32>>,
    pass_shader: Option<Shader>,
    empty_texture: Option<Texture>,
    fonts_texture: Option<Texture>,
    shaders_stack: Vec<Shader>,
    transform_stack: Vec<Transform<f32, f32, f32>>,
    blending_stack: Vec<GlowBlending>,
}

impl DrawContext {
    pub fn begin_frame(&mut self, graphics: &mut Graphics<Vertex>) {
        if self.pass_shader.is_none() {
            self.pass_shader = graphics
                .shader(Shader::PASS_VERTEX_2D, Shader::PASS_FRAGMENT)
                .ok();
        }
        if self.empty_texture.is_none() {
            self.empty_texture = graphics.pixel_texture([255, 255, 255]).ok();
        }
        if self.fonts_texture.is_none() {
            self.fonts_texture = graphics.pixel_texture([255, 255, 255]).ok();
        }
        self.text_renderer.clear();
        self.shaders_stack.clear();
        self.transform_stack.clear();
        self.blending_stack.clear();
    }

    pub fn end_frame(&mut self) {
        let [width, height, depth] = self.text_renderer.atlas_size();
        if let Some(fonts_texture) = self.fonts_texture.as_mut() {
            fonts_texture.upload(
                width as _,
                height as _,
                depth as _,
                GlowTextureFormat::Monochromatic,
                Some(self.text_renderer.image()),
            );
        }
    }

    pub fn shader(&self, reference: Option<&ShaderRef>) -> Option<Shader> {
        reference
            .and_then(|reference| match reference {
                ResourceRef::Name(name) => self.shaders.get(name).cloned(),
                ResourceRef::Object(object) => Some(object.to_owned()),
            })
            .or_else(|| self.shaders_stack.last().cloned())
    }

    pub fn shader_or_pass(&self, reference: Option<&ShaderRef>) -> Option<Shader> {
        self.shader(reference).or_else(|| self.pass_shader.clone())
    }

    pub fn texture(&self, reference: Option<&TextureRef>) -> Option<Texture> {
        reference.and_then(|reference| match reference {
            ResourceRef::Name(name) => self.textures.get(name).cloned(),
            ResourceRef::Object(object) => Some(object.to_owned()),
        })
    }

    pub fn texture_or_empty(&self, reference: Option<&TextureRef>) -> Option<Texture> {
        self.texture(reference)
            .or_else(|| self.empty_texture.clone())
    }

    pub fn pass_shader(&self) -> Option<Shader> {
        self.pass_shader.clone()
    }

    pub fn empty_texture(&self) -> Option<Texture> {
        self.empty_texture.clone()
    }

    pub fn fonts_texture(&self) -> Option<Texture> {
        self.fonts_texture.clone()
    }

    pub fn push_shader(&mut self, shader: &ShaderRef) {
        match shader {
            ResourceRef::Name(name) => {
                if let Some(shader) = self.shaders.get(name) {
                    self.shaders_stack.push(shader.clone());
                }
            }
            ResourceRef::Object(object) => {
                self.shaders_stack.push(object.clone());
            }
        }
    }

    pub fn pop_shader(&mut self) -> Option<Shader> {
        self.shaders_stack.pop()
    }

    pub fn top_shader(&self) -> Option<Shader> {
        self.shaders_stack.last().cloned()
    }

    pub fn with_shader<R>(&mut self, shader: &ShaderRef, mut f: impl FnMut() -> R) -> R {
        self.push_shader(shader);
        let result = f();
        self.pop_shader();
        result
    }

    pub fn push_transform(&mut self, transform: Transform<f32, f32, f32>) {
        self.transform_stack.push(transform);
    }

    pub fn pop_transform(&mut self) -> Option<Transform<f32, f32, f32>> {
        self.transform_stack.pop()
    }

    pub fn top_transform(&self) -> Transform<f32, f32, f32> {
        self.transform_stack.last().copied().unwrap_or_default()
    }

    pub fn with_transform<R>(
        &mut self,
        transform: Transform<f32, f32, f32>,
        mut f: impl FnMut() -> R,
    ) -> R {
        self.push_transform(transform);
        let result = f();
        self.pop_transform();
        result
    }

    pub fn push_blending(&mut self, blending: GlowBlending) {
        self.blending_stack.push(blending);
    }

    pub fn pop_blending(&mut self) -> Option<GlowBlending> {
        self.blending_stack.pop()
    }

    pub fn top_blending(&self) -> GlowBlending {
        self.blending_stack.last().copied().unwrap_or_default()
    }

    pub fn with_blending<R>(&mut self, blending: GlowBlending, mut f: impl FnMut() -> R) -> R {
        self.push_blending(blending);
        let result = f();
        self.pop_blending();
        result
    }
}