good_web_game/graphics/
context.rs

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        // pipeline has to be applied whenever the shader (and therefore pipeline) changes
70        quad_ctx.apply_pipeline(&default_pipeline);
71
72        // Glyph cache stuff.
73        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    /// Sets the raw projection matrix to the given Matrix.
113    ///
114    /// Call `update_globals()` to apply after calling this.
115    pub(crate) fn set_projection(&mut self, mat: Matrix4<f32>) {
116        self.projection = mat;
117    }
118
119    /// Gets a copy of the raw projection matrix.
120    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    /// Gets the current global
135    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    /// As the default shader is created first it holds the first id, which is 0.
145    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}