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}