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