spitfire_draw/
context.rs

1use crate::utils::{FontMap, ResourceRef, ShaderRef, TextureRef, Vertex};
2use spitfire_fontdue::TextRenderer;
3use spitfire_glow::{
4    graphics::{Graphics, Shader, Texture},
5    renderer::{GlowBlending, GlowTextureFormat},
6};
7use std::{borrow::Cow, collections::HashMap};
8use vek::{Rgba, Transform};
9
10#[derive(Default, Clone)]
11pub struct DrawContext {
12    pub shaders: HashMap<Cow<'static, str>, Shader>,
13    pub textures: HashMap<Cow<'static, str>, Texture>,
14    pub fonts: FontMap,
15    pub text_renderer: TextRenderer<Rgba<f32>>,
16    pass_shader: Option<Shader>,
17    empty_texture: Option<Texture>,
18    fonts_texture: Option<Texture>,
19    shaders_stack: Vec<Shader>,
20    transform_stack: Vec<Transform<f32, f32, f32>>,
21    blending_stack: Vec<GlowBlending>,
22}
23
24impl DrawContext {
25    pub fn begin_frame(&mut self, graphics: &mut Graphics<Vertex>) {
26        if self.pass_shader.is_none() {
27            self.pass_shader = graphics
28                .shader(Shader::PASS_VERTEX_2D, Shader::PASS_FRAGMENT)
29                .ok();
30        }
31        if self.empty_texture.is_none() {
32            self.empty_texture = graphics.pixel_texture([255, 255, 255]).ok();
33        }
34        if self.fonts_texture.is_none() {
35            self.fonts_texture = graphics.pixel_texture([255, 255, 255]).ok();
36        }
37        self.text_renderer.clear();
38        self.shaders_stack.clear();
39        self.transform_stack.clear();
40        self.blending_stack.clear();
41    }
42
43    pub fn end_frame(&mut self) {
44        let [width, height, depth] = self.text_renderer.atlas_size();
45        if let Some(fonts_texture) = self.fonts_texture.as_mut() {
46            fonts_texture.upload(
47                width as _,
48                height as _,
49                depth as _,
50                GlowTextureFormat::Monochromatic,
51                Some(self.text_renderer.image()),
52            );
53        }
54    }
55
56    pub fn shader(&self, reference: Option<&ShaderRef>) -> Option<Shader> {
57        reference
58            .and_then(|reference| match reference {
59                ResourceRef::Name(name) => self.shaders.get(name).cloned(),
60                ResourceRef::Object(object) => Some(object.to_owned()),
61            })
62            .or_else(|| self.shaders_stack.last().cloned())
63    }
64
65    pub fn shader_or_pass(&self, reference: Option<&ShaderRef>) -> Option<Shader> {
66        self.shader(reference).or_else(|| self.pass_shader.clone())
67    }
68
69    pub fn texture(&self, reference: Option<&TextureRef>) -> Option<Texture> {
70        reference.and_then(|reference| match reference {
71            ResourceRef::Name(name) => self.textures.get(name).cloned(),
72            ResourceRef::Object(object) => Some(object.to_owned()),
73        })
74    }
75
76    pub fn texture_or_empty(&self, reference: Option<&TextureRef>) -> Option<Texture> {
77        self.texture(reference)
78            .or_else(|| self.empty_texture.clone())
79    }
80
81    pub fn pass_shader(&self) -> Option<Shader> {
82        self.pass_shader.clone()
83    }
84
85    pub fn empty_texture(&self) -> Option<Texture> {
86        self.empty_texture.clone()
87    }
88
89    pub fn fonts_texture(&self) -> Option<Texture> {
90        self.fonts_texture.clone()
91    }
92
93    pub fn push_shader(&mut self, shader: &ShaderRef) {
94        match shader {
95            ResourceRef::Name(name) => {
96                if let Some(shader) = self.shaders.get(name) {
97                    self.shaders_stack.push(shader.clone());
98                }
99            }
100            ResourceRef::Object(object) => {
101                self.shaders_stack.push(object.clone());
102            }
103        }
104    }
105
106    pub fn pop_shader(&mut self) -> Option<Shader> {
107        self.shaders_stack.pop()
108    }
109
110    pub fn top_shader(&self) -> Option<Shader> {
111        self.shaders_stack.last().cloned()
112    }
113
114    pub fn with_shader<R>(&mut self, shader: &ShaderRef, mut f: impl FnMut() -> R) -> R {
115        self.push_shader(shader);
116        let result = f();
117        self.pop_shader();
118        result
119    }
120
121    pub fn push_transform(&mut self, transform: Transform<f32, f32, f32>) {
122        self.transform_stack.push(transform);
123    }
124
125    pub fn pop_transform(&mut self) -> Option<Transform<f32, f32, f32>> {
126        self.transform_stack.pop()
127    }
128
129    pub fn top_transform(&self) -> Transform<f32, f32, f32> {
130        self.transform_stack.last().copied().unwrap_or_default()
131    }
132
133    pub fn with_transform<R>(
134        &mut self,
135        transform: Transform<f32, f32, f32>,
136        mut f: impl FnMut() -> R,
137    ) -> R {
138        self.push_transform(transform);
139        let result = f();
140        self.pop_transform();
141        result
142    }
143
144    pub fn push_blending(&mut self, blending: GlowBlending) {
145        self.blending_stack.push(blending);
146    }
147
148    pub fn pop_blending(&mut self) -> Option<GlowBlending> {
149        self.blending_stack.pop()
150    }
151
152    pub fn top_blending(&self) -> GlowBlending {
153        self.blending_stack.last().copied().unwrap_or_default()
154    }
155
156    pub fn with_blending<R>(&mut self, blending: GlowBlending, mut f: impl FnMut() -> R) -> R {
157        self.push_blending(blending);
158        let result = f();
159        self.pop_blending();
160        result
161    }
162}