1use crate::graphics::{types::Rect, Canvas, FilterMode, Shader, ShaderId};
2use std::rc::Rc;
3
4use crate::graphics::{spritebatch, BlendMode, DrawParam, Font, Image};
5use cgmath::Matrix4;
6use glyph_brush::{GlyphBrush, GlyphBrushBuilder};
7use miniquad::{BufferLayout, PipelineParams, Texture, VertexAttribute, VertexFormat, VertexStep};
8use std::cell::RefCell;
9
10pub struct GraphicsContext {
11 pub(crate) screen_rect: Rect,
12 pub(crate) projection: Matrix4<f32>,
13 pub(crate) white_texture: miniquad::Texture,
14 pub(crate) canvas: Option<Canvas>,
15 pub(crate) current_shader: Rc<RefCell<ShaderId>>,
16 pub(crate) shaders: Vec<Shader>,
17 pub(crate) blend_mode: BlendMode,
18 pub(crate) default_filter: FilterMode,
19
20 pub(crate) glyph_brush: Rc<RefCell<GlyphBrush<DrawParam>>>,
21 pub(crate) glyph_cache: Image,
22 pub(crate) glyph_state: Rc<RefCell<spritebatch::SpriteBatch>>,
23}
24
25impl GraphicsContext {
26 pub fn new(quad_ctx: &mut miniquad::graphics::GraphicsContext) -> GraphicsContext {
27 let projection = cgmath::One::one();
28 let screen_rect = Rect::new(-1., -1., 2., 2.);
29
30 let white_texture = Texture::from_rgba8(quad_ctx, 1, 1, &[255, 255, 255, 255]);
31 let (color_blend, alpha_blend) = BlendMode::Alpha.into();
32
33 let default_shader = miniquad::Shader::new(
34 quad_ctx,
35 default_shader::VERTEX,
36 default_shader::FRAGMENT,
37 default_shader::meta(),
38 )
39 .expect("couldn't create default shader");
40
41 let gwg_default_shader =
42 crate::graphics::Shader::from_mini_shader(quad_ctx, default_shader, None);
43
44 let default_pipeline = miniquad::Pipeline::with_params(
45 quad_ctx,
46 &[
47 BufferLayout::default(),
48 BufferLayout {
49 step_func: VertexStep::PerInstance,
50 ..Default::default()
51 },
52 ],
53 &[
54 VertexAttribute::with_buffer("position", VertexFormat::Float2, 0),
55 VertexAttribute::with_buffer("texcoord", VertexFormat::Float2, 0),
56 VertexAttribute::with_buffer("color0", VertexFormat::Float4, 0),
57 VertexAttribute::with_buffer("Source", VertexFormat::Float4, 1),
58 VertexAttribute::with_buffer("Color", VertexFormat::Float4, 1),
59 VertexAttribute::with_buffer("Model", VertexFormat::Mat4, 1),
60 ],
61 default_shader,
62 PipelineParams {
63 color_blend: Some(color_blend),
64 alpha_blend: Some(alpha_blend),
65 ..Default::default()
66 },
67 );
68
69 quad_ctx.apply_pipeline(&default_pipeline);
71
72 let font_vec = glyph_brush::ab_glyph::FontArc::try_from_slice(Font::default_font_bytes())
74 .expect("Invalid default font bytes, should never happen");
75 let glyph_brush = GlyphBrushBuilder::using_font(font_vec).build();
76 let (glyph_cache_width, glyph_cache_height) = glyph_brush.texture_dimensions();
77 let initial_contents = vec![
78 255;
79 4 * usize::try_from(glyph_cache_width).unwrap()
80 * usize::try_from(glyph_cache_height).unwrap()
81 ];
82 let glyph_cache = Texture::from_rgba8(
83 quad_ctx,
84 glyph_cache_width.try_into().unwrap(),
85 glyph_cache_height.try_into().unwrap(),
86 &initial_contents,
87 );
88
89 let glyph_cache = Image::from_texture(quad_ctx, glyph_cache, FilterMode::Linear).unwrap();
90
91 let glyph_state = Rc::new(RefCell::new(spritebatch::SpriteBatch::new(
92 glyph_cache.clone(),
93 )));
94
95 GraphicsContext {
96 projection,
97 screen_rect,
98 white_texture,
99 canvas: None,
100 current_shader: Rc::new(RefCell::new(0)),
101 shaders: vec![gwg_default_shader],
102 blend_mode: BlendMode::Alpha,
103 default_filter: FilterMode::Linear,
104 glyph_brush: Rc::new(RefCell::new(glyph_brush)),
105 glyph_cache,
106 glyph_state,
107 }
108 }
109}
110
111impl GraphicsContext {
112 pub(crate) fn set_projection(&mut self, mat: Matrix4<f32>) {
116 self.projection = mat;
117 }
118
119 pub(crate) fn projection(&self) -> Matrix4<f32> {
121 self.projection
122 }
123
124 pub fn set_screen_coordinates(&mut self, rect: crate::graphics::types::Rect) {
125 self.screen_rect = rect;
126 self.projection =
127 cgmath::ortho(rect.x, rect.x + rect.w, rect.y + rect.h, rect.y, -1.0, 1.0);
128 }
129
130 pub(crate) fn set_blend_mode(&mut self, mode: BlendMode) {
131 self.blend_mode = mode;
132 }
133
134 pub(crate) fn blend_mode(&self) -> &BlendMode {
136 &self.blend_mode
137 }
138}
139
140pub(crate) mod default_shader {
141 use crate::graphics::ShaderId;
142 use miniquad::{ShaderMeta, UniformBlockLayout, UniformDesc, UniformType};
143
144 pub const SHADER_ID: ShaderId = 0;
146
147 pub const VERTEX: &str = r#"#version 100
148 attribute vec2 position;
149 attribute vec2 texcoord;
150 attribute vec4 color0;
151
152 attribute vec4 Source;
153 attribute vec4 Color;
154 attribute mat4 Model;
155
156 varying lowp vec4 color;
157 varying lowp vec2 uv;
158
159 uniform mat4 Projection;
160
161 uniform float depth;
162
163 void main() {
164 gl_Position = Projection * Model * vec4(position, 0, 1);
165 gl_Position.z = depth;
166 color = Color * color0;
167 uv = texcoord * Source.zw + Source.xy;
168 }"#;
169
170 pub const FRAGMENT: &str = r#"#version 100
171 varying lowp vec4 color;
172 varying lowp vec2 uv;
173
174 uniform sampler2D Texture;
175
176 void main() {
177 gl_FragColor = texture2D(Texture, uv) * color;
178 }"#;
179
180 pub fn meta() -> ShaderMeta {
181 ShaderMeta {
182 images: vec!["Texture".to_string()],
183 uniforms: UniformBlockLayout {
184 uniforms: vec![UniformDesc::new("Projection", UniformType::Mat4)],
185 },
186 }
187 }
188}