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}