quad_gl/
lib.rs

1use miniquad::*;
2
3pub use colors::*;
4
5pub use miniquad::{FilterMode, ShaderError};
6
7const UNIFORMS_ARRAY_SIZE: usize = 512;
8
9#[repr(C)]
10#[derive(Clone, Copy, Debug, Default, PartialEq)]
11pub struct Color {
12    pub r: f32,
13    pub g: f32,
14    pub b: f32,
15    pub a: f32,
16}
17
18/// Build a color from 4 components of 0..255 values
19/// This is a temporary solution and going to be replaced with const fn,
20/// waiting for https://github.com/rust-lang/rust/issues/57241
21#[macro_export]
22macro_rules! color_u8 {
23    ($r:expr, $g:expr, $b:expr, $a:expr) => {
24        Color::new(
25            $r as f32 / 255.,
26            $g as f32 / 255.,
27            $b as f32 / 255.,
28            $a as f32 / 255.,
29        )
30    };
31}
32
33#[test]
34fn color_from_bytes() {
35    assert_eq!(Color::new(1.0, 0.0, 0.0, 1.0), color_u8!(255, 0, 0, 255));
36    assert_eq!(
37        Color::new(1.0, 0.5, 0.0, 1.0),
38        color_u8!(255, 127.5, 0, 255)
39    );
40    assert_eq!(
41        Color::new(0.0, 1.0, 0.5, 1.0),
42        color_u8!(0, 255, 127.5, 255)
43    );
44}
45
46impl Into<[u8; 4]> for Color {
47    fn into(self) -> [u8; 4] {
48        [
49            (self.r * 255.) as u8,
50            (self.g * 255.) as u8,
51            (self.b * 255.) as u8,
52            (self.a * 255.) as u8,
53        ]
54    }
55}
56
57impl Into<Color> for [u8; 4] {
58    fn into(self) -> Color {
59        Color::new(
60            self[0] as f32 / 255.,
61            self[1] as f32 / 255.,
62            self[2] as f32 / 255.,
63            self[3] as f32 / 255.,
64        )
65    }
66}
67
68impl Into<[f32; 4]> for Color {
69    fn into(self) -> [f32; 4] {
70        [self.r, self.g, self.b, self.a]
71    }
72}
73
74impl From<[f32; 4]> for Color {
75    fn from(colors: [f32; 4]) -> Color {
76        Color::new(colors[0], colors[1], colors[2], colors[3])
77    }
78}
79
80impl Color {
81    pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
82        Color { r, g, b, a }
83    }
84
85    pub fn to_vec(&self) -> glam::Vec4 {
86        glam::Vec4::new(self.r, self.g, self.b, self.a)
87    }
88
89    pub fn from_vec(vec: glam::Vec4) -> Self {
90        Self::new(vec.x, vec.y, vec.z, vec.w)
91    }
92}
93
94pub mod colors {
95    //! Constants for some common colors.
96
97    use super::Color;
98
99    pub const LIGHTGRAY: Color = Color::new(0.78, 0.78, 0.78, 1.00);
100    pub const GRAY: Color = Color::new(0.51, 0.51, 0.51, 1.00);
101    pub const DARKGRAY: Color = Color::new(0.31, 0.31, 0.31, 1.00);
102    pub const YELLOW: Color = Color::new(0.99, 0.98, 0.00, 1.00);
103    pub const GOLD: Color = Color::new(1.00, 0.80, 0.00, 1.00);
104    pub const ORANGE: Color = Color::new(1.00, 0.63, 0.00, 1.00);
105    pub const PINK: Color = Color::new(1.00, 0.43, 0.76, 1.00);
106    pub const RED: Color = Color::new(0.90, 0.16, 0.22, 1.00);
107    pub const MAROON: Color = Color::new(0.75, 0.13, 0.22, 1.00);
108    pub const GREEN: Color = Color::new(0.00, 0.89, 0.19, 1.00);
109    pub const LIME: Color = Color::new(0.00, 0.62, 0.18, 1.00);
110    pub const DARKGREEN: Color = Color::new(0.00, 0.46, 0.17, 1.00);
111    pub const SKYBLUE: Color = Color::new(0.40, 0.75, 1.00, 1.00);
112    pub const BLUE: Color = Color::new(0.00, 0.47, 0.95, 1.00);
113    pub const DARKBLUE: Color = Color::new(0.00, 0.32, 0.67, 1.00);
114    pub const PURPLE: Color = Color::new(0.78, 0.48, 1.00, 1.00);
115    pub const VIOLET: Color = Color::new(0.53, 0.24, 0.75, 1.00);
116    pub const DARKPURPLE: Color = Color::new(0.44, 0.12, 0.49, 1.00);
117    pub const BEIGE: Color = Color::new(0.83, 0.69, 0.51, 1.00);
118    pub const BROWN: Color = Color::new(0.50, 0.42, 0.31, 1.00);
119    pub const DARKBROWN: Color = Color::new(0.30, 0.25, 0.18, 1.00);
120    pub const WHITE: Color = Color::new(1.00, 1.00, 1.00, 1.00);
121    pub const BLACK: Color = Color::new(0.00, 0.00, 0.00, 1.00);
122    pub const BLANK: Color = Color::new(0.00, 0.00, 0.00, 0.00);
123    pub const MAGENTA: Color = Color::new(1.00, 0.00, 1.00, 1.00);
124}
125
126const MAX_VERTICES: usize = 10000;
127const MAX_INDICES: usize = 5000;
128
129#[derive(Debug, Clone, Copy, PartialEq)]
130pub enum DrawMode {
131    Triangles,
132    Lines,
133}
134
135#[derive(Debug, Clone, Copy, PartialEq)]
136pub struct GlPipeline(usize);
137
138struct DrawCall {
139    vertices: [Vertex; MAX_VERTICES],
140    indices: [u16; MAX_INDICES],
141
142    vertices_count: usize,
143    indices_count: usize,
144
145    clip: Option<(i32, i32, i32, i32)>,
146    texture: Texture,
147
148    model: glam::Mat4,
149    projection: glam::Mat4,
150
151    draw_mode: DrawMode,
152    pipeline: GlPipeline,
153    render_pass: Option<RenderPass>,
154}
155
156#[repr(C)]
157#[derive(Debug, Clone, Copy, Default)]
158pub struct Vertex {
159    pos: [f32; 3],
160    uv: [f32; 2],
161    color: [u8; 4],
162}
163
164pub type VertexInterop = ([f32; 3], [f32; 2], [f32; 4]);
165
166impl Into<VertexInterop> for Vertex {
167    fn into(self) -> VertexInterop {
168        (
169            self.pos,
170            self.uv,
171            [
172                self.color[0] as f32 / 255.0,
173                self.color[1] as f32 / 255.0,
174                self.color[2] as f32 / 255.0,
175                self.color[3] as f32 / 255.0,
176            ],
177        )
178    }
179}
180impl Into<Vertex> for VertexInterop {
181    fn into(self) -> Vertex {
182        Vertex {
183            pos: self.0,
184            uv: self.1,
185            color: [
186                ((self.2)[0] * 255.) as u8,
187                ((self.2)[1] * 255.) as u8,
188                ((self.2)[2] * 255.) as u8,
189                ((self.2)[3] * 255.) as u8,
190            ],
191        }
192    }
193}
194
195impl Vertex {
196    pub fn new(x: f32, y: f32, z: f32, u: f32, v: f32, color: Color) -> Vertex {
197        Vertex {
198            pos: [x, y, z],
199            uv: [u, v],
200            color: [
201                (color.r * 255.) as u8,
202                (color.g * 255.) as u8,
203                (color.b * 255.) as u8,
204                (color.a * 255.) as u8,
205            ],
206        }
207    }
208}
209
210impl DrawCall {
211    fn new(
212        texture: Texture,
213        projection: glam::Mat4,
214        model: glam::Mat4,
215        draw_mode: DrawMode,
216        pipeline: GlPipeline,
217        render_pass: Option<RenderPass>,
218    ) -> DrawCall {
219        DrawCall {
220            vertices: [Vertex::new(0., 0., 0., 0., 0., Color::new(0.0, 0.0, 0.0, 0.0));
221                MAX_VERTICES],
222            indices: [0; MAX_INDICES],
223            vertices_count: 0,
224            indices_count: 0,
225            clip: None,
226            texture,
227            projection,
228            model,
229            draw_mode,
230            pipeline,
231            render_pass,
232        }
233    }
234
235    fn vertices(&self) -> &[Vertex] {
236        &self.vertices[0..self.vertices_count]
237    }
238
239    fn indices(&self) -> &[u16] {
240        &self.indices[0..self.indices_count]
241    }
242}
243
244struct MagicSnapshoter {
245    pipeline: Pipeline,
246    bindings: Bindings,
247    pass: Option<RenderPass>,
248
249    screen_texture: Option<Texture2D>,
250}
251
252mod snapshoter_shader {
253    use miniquad::{ShaderMeta, UniformBlockLayout};
254
255    pub const VERTEX: &str = r#"#version 100
256    attribute vec2 position;
257    attribute vec2 texcoord;
258
259    varying lowp vec2 uv;
260
261    void main() {
262        gl_Position = vec4(position, 0, 1);
263        uv = texcoord;
264    }"#;
265
266    pub const FRAGMENT: &str = r#"#version 100
267    varying lowp vec2 uv;
268    
269    uniform sampler2D Texture;
270
271    void main() {
272        gl_FragColor = texture2D(Texture, uv);
273    }"#;
274
275    pub fn meta() -> ShaderMeta {
276        ShaderMeta {
277            images: vec!["Texture".to_string()],
278            uniforms: UniformBlockLayout { uniforms: vec![] },
279        }
280    }
281
282    #[repr(C)]
283    #[derive(Debug)]
284    pub struct Uniforms {}
285}
286
287impl MagicSnapshoter {
288    fn new(ctx: &mut Context) -> MagicSnapshoter {
289        let shader = Shader::new(
290            ctx,
291            snapshoter_shader::VERTEX,
292            snapshoter_shader::FRAGMENT,
293            snapshoter_shader::meta(),
294        )
295        .unwrap_or_else(|e| panic!("Failed to load shader: {}", e));
296
297        let pipeline = Pipeline::with_params(
298            ctx,
299            &[BufferLayout::default()],
300            &[
301                VertexAttribute::new("position", VertexFormat::Float2),
302                VertexAttribute::new("texcoord", VertexFormat::Float2),
303            ],
304            shader,
305            PipelineParams::default(),
306        );
307
308        #[rustfmt::skip]
309        let vertices: [f32; 16] = [
310             -1.0, -1.0, 0., 0.,
311             1.0, -1.0, 1., 0. ,
312             1.0,  1.0, 1., 1. ,
313            -1.0,  1.0, 0., 1. ,
314        ];
315        let vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, &vertices);
316
317        let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
318        let index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, &indices);
319
320        let bindings = Bindings {
321            vertex_buffers: vec![vertex_buffer],
322            index_buffer,
323            images: vec![Texture::empty()],
324        };
325
326        MagicSnapshoter {
327            pipeline,
328            bindings,
329            pass: None,
330            screen_texture: None,
331        }
332    }
333
334    fn snapshot(&mut self, ctx: &mut Context, camera_render_pass: Option<RenderPass>) {
335        if let Some(camera_render_pass) = camera_render_pass {
336            let texture = camera_render_pass.texture(ctx);
337            if self.pass.is_none() {
338                let color_img = Texture::new_render_texture(
339                    ctx,
340                    TextureParams {
341                        width: texture.width,
342                        height: texture.height,
343                        format: texture.format,
344                        ..Default::default()
345                    },
346                );
347
348                self.pass = Some(RenderPass::new(ctx, color_img, None));
349                self.screen_texture = Some(Texture2D::from_miniquad_texture(color_img));
350            }
351
352            if self.bindings.images.len() == 0 {
353                self.bindings.images.push(texture);
354            } else {
355                self.bindings.images[0] = texture;
356            }
357            ctx.begin_pass(
358                self.pass.unwrap(),
359                PassAction::clear_color(1.0, 0.0, 1.0, 1.),
360            );
361            ctx.apply_pipeline(&self.pipeline);
362            ctx.apply_bindings(&self.bindings);
363            ctx.draw(0, 6, 1);
364            ctx.end_render_pass();
365        } else {
366            let (screen_width, screen_height) = ctx.screen_size();
367            if self.screen_texture.is_none()
368                || self
369                    .screen_texture
370                    .map(|t| {
371                        t.texture.width != screen_width as _
372                            || t.texture.height != screen_height as _
373                    })
374                    .unwrap_or(false)
375            {
376                self.screen_texture = Some(Texture2D::from_miniquad_texture(
377                    Texture::new_render_texture(
378                        ctx,
379                        TextureParams {
380                            width: screen_width as _,
381                            height: screen_height as _,
382                            ..Default::default()
383                        },
384                    ),
385                ))
386            }
387
388            let texture = self.screen_texture.unwrap();
389            texture.grab_screen();
390        }
391    }
392}
393
394struct GlState {
395    texture: Texture,
396    draw_mode: DrawMode,
397    clip: Option<(i32, i32, i32, i32)>,
398    projection: glam::Mat4,
399    model_stack: Vec<glam::Mat4>,
400    pipeline: Option<GlPipeline>,
401    depth_test_enable: bool,
402
403    snapshoter: MagicSnapshoter,
404
405    render_pass: Option<RenderPass>,
406}
407
408impl GlState {
409    fn model(&self) -> glam::Mat4 {
410        *self.model_stack.last().unwrap()
411    }
412}
413
414#[derive(Clone)]
415struct Uniform {
416    name: String,
417    uniform_type: UniformType,
418    byte_offset: usize,
419}
420
421#[derive(Clone)]
422struct PipelineExt {
423    pipeline: miniquad::Pipeline,
424    wants_screen_texture: bool,
425    uniforms: Vec<Uniform>,
426    uniforms_data: [u8; UNIFORMS_ARRAY_SIZE],
427}
428
429struct PipelinesStorage {
430    pipelines: [Option<PipelineExt>; Self::MAX_PIPELINES],
431    pipelines_amount: usize,
432}
433
434impl PipelinesStorage {
435    const MAX_PIPELINES: usize = 32;
436    const TRIANGLES_PIPELINE: GlPipeline = GlPipeline(0);
437    const LINES_PIPELINE: GlPipeline = GlPipeline(1);
438    const TRIANGLES_DEPTH_PIPELINE: GlPipeline = GlPipeline(2);
439    const LINES_DEPTH_PIPELINE: GlPipeline = GlPipeline(3);
440
441    fn new(ctx: &mut miniquad::Context) -> PipelinesStorage {
442        let shader = Shader::new(ctx, shader::VERTEX, shader::FRAGMENT, shader::meta())
443            .unwrap_or_else(|e| panic!("Failed to load shader: {}", e));
444
445        let params = PipelineParams {
446            color_blend: Some(BlendState::new(
447                Equation::Add,
448                BlendFactor::Value(BlendValue::SourceAlpha),
449                BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
450            )),
451            ..Default::default()
452        };
453
454        let mut storage = PipelinesStorage {
455            pipelines: Default::default(),
456            pipelines_amount: 0,
457        };
458
459        let triangles_pipeline = storage.make_pipeline(
460            ctx,
461            shader,
462            PipelineParams {
463                primitive_type: PrimitiveType::Triangles,
464                ..params
465            },
466            false,
467            vec![],
468        );
469        assert_eq!(triangles_pipeline, Self::TRIANGLES_PIPELINE);
470
471        let lines_pipeline = storage.make_pipeline(
472            ctx,
473            shader,
474            PipelineParams {
475                primitive_type: PrimitiveType::Lines,
476                ..params
477            },
478            false,
479            vec![],
480        );
481        assert_eq!(lines_pipeline, Self::LINES_PIPELINE);
482
483        let triangles_depth_pipeline = storage.make_pipeline(
484            ctx,
485            shader,
486            PipelineParams {
487                depth_write: true,
488                depth_test: Comparison::LessOrEqual,
489                primitive_type: PrimitiveType::Triangles,
490                ..params
491            },
492            false,
493            vec![],
494        );
495        assert_eq!(triangles_depth_pipeline, Self::TRIANGLES_DEPTH_PIPELINE);
496
497        let lines_depth_pipeline = storage.make_pipeline(
498            ctx,
499            shader,
500            PipelineParams {
501                depth_write: true,
502                depth_test: Comparison::LessOrEqual,
503                primitive_type: PrimitiveType::Lines,
504                ..params
505            },
506            false,
507            vec![],
508        );
509        assert_eq!(lines_depth_pipeline, Self::LINES_DEPTH_PIPELINE);
510
511        storage
512    }
513
514    fn make_pipeline(
515        &mut self,
516        ctx: &mut Context,
517        shader: Shader,
518        params: PipelineParams,
519        wants_screen_texture: bool,
520        uniforms: Vec<(String, UniformType)>,
521    ) -> GlPipeline {
522        let pipeline = Pipeline::with_params(
523            ctx,
524            &[BufferLayout::default()],
525            &[
526                VertexAttribute::new("position", VertexFormat::Float3),
527                VertexAttribute::new("texcoord", VertexFormat::Float2),
528                VertexAttribute::new("color0", VertexFormat::Byte4),
529            ],
530            shader,
531            params,
532        );
533
534        let id = self
535            .pipelines
536            .iter()
537            .position(|p| p.is_none())
538            .unwrap_or_else(|| panic!("Pipelines amount exceeded"));
539
540        let uniforms = uniforms
541            .iter()
542            .scan(0, |offset, uniform| {
543                let uniform_byte_size = uniform.1.size();
544                if *offset + uniform_byte_size > UNIFORMS_ARRAY_SIZE {
545                    println!(
546                        "Material exceeds maximum uniforms amount, uniforms after {} skipped",
547                        uniform.0
548                    );
549                    return None;
550                }
551                let uniform = Uniform {
552                    name: uniform.0.clone(),
553                    uniform_type: uniform.1,
554                    byte_offset: *offset,
555                };
556                *offset += uniform_byte_size;
557
558                Some(uniform)
559            })
560            .collect();
561        self.pipelines[id] = Some(PipelineExt {
562            pipeline,
563            wants_screen_texture,
564            uniforms,
565            uniforms_data: [0; UNIFORMS_ARRAY_SIZE],
566        });
567        self.pipelines_amount += 1;
568
569        GlPipeline(id)
570    }
571
572    fn get(&self, draw_mode: DrawMode, depth_enabled: bool) -> GlPipeline {
573        match (draw_mode, depth_enabled) {
574            (DrawMode::Triangles, false) => Self::TRIANGLES_PIPELINE,
575            (DrawMode::Triangles, true) => Self::TRIANGLES_DEPTH_PIPELINE,
576            (DrawMode::Lines, false) => Self::LINES_PIPELINE,
577            (DrawMode::Lines, true) => Self::LINES_DEPTH_PIPELINE,
578        }
579    }
580
581    fn get_quad_pipeline(&self, pip: GlPipeline) -> &PipelineExt {
582        &self.pipelines[pip.0].as_ref().unwrap()
583    }
584
585    fn get_quad_pipeline_mut(&mut self, pip: GlPipeline) -> &mut PipelineExt {
586        self.pipelines[pip.0].as_mut().unwrap()
587    }
588
589    fn delete_pipeline(&mut self, pip: GlPipeline) {
590        self.pipelines[pip.0] = None;
591    }
592}
593
594pub struct QuadGl {
595    pipelines: PipelinesStorage,
596
597    draw_calls: Vec<DrawCall>,
598    draw_calls_bindings: Vec<Bindings>,
599    draw_calls_count: usize,
600    state: GlState,
601    start_time: f64,
602
603    white_texture: Texture,
604}
605
606impl QuadGl {
607    pub fn new(ctx: &mut miniquad::Context) -> QuadGl {
608        let white_texture = Texture::from_rgba8(ctx, 1, 1, &[255, 255, 255, 255]);
609
610        QuadGl {
611            pipelines: PipelinesStorage::new(ctx),
612            state: GlState {
613                clip: None,
614                texture: white_texture,
615                projection: glam::Mat4::identity(),
616                model_stack: vec![glam::Mat4::identity()],
617                draw_mode: DrawMode::Triangles,
618                pipeline: None,
619                depth_test_enable: false,
620                snapshoter: MagicSnapshoter::new(ctx),
621                render_pass: None,
622            },
623            draw_calls: Vec::with_capacity(200),
624            draw_calls_bindings: Vec::with_capacity(200),
625            draw_calls_count: 0,
626            start_time: miniquad::date::now(),
627            white_texture,
628        }
629    }
630
631    pub fn make_pipeline(
632        &mut self,
633        ctx: &mut Context,
634        vertex_shader: &str,
635        fragment_shader: &str,
636        params: PipelineParams,
637        uniforms: Vec<(String, UniformType)>,
638    ) -> Result<GlPipeline, ShaderError> {
639        let mut shader_meta: ShaderMeta = shader::meta();
640
641        for uniform in &uniforms {
642            shader_meta
643                .uniforms
644                .uniforms
645                .push(UniformDesc::new(&uniform.0, uniform.1));
646        }
647
648        let shader = Shader::new(ctx, vertex_shader, fragment_shader, shader_meta)?;
649        let wants_screen_texture = fragment_shader.find("_ScreenTexture").is_some();
650
651        Ok(self
652            .pipelines
653            .make_pipeline(ctx, shader, params, wants_screen_texture, uniforms))
654    }
655
656    /// Reset only draw calls state
657    pub fn clear_draw_calls(&mut self) {
658        self.draw_calls_count = 0;
659    }
660
661    /// Reset internal state to known default
662    pub fn reset(&mut self) {
663        self.state.clip = None;
664        self.state.texture = self.white_texture;
665        self.state.projection = glam::Mat4::identity();
666        self.state.model_stack = vec![glam::Mat4::identity()];
667
668        self.draw_calls_count = 0;
669    }
670
671    pub fn draw(&mut self, ctx: &mut miniquad::Context) {
672        for _ in 0..self.draw_calls.len() - self.draw_calls_bindings.len() {
673            let vertex_buffer = Buffer::stream(
674                ctx,
675                BufferType::VertexBuffer,
676                MAX_VERTICES * std::mem::size_of::<Vertex>(),
677            );
678            let index_buffer = Buffer::stream(
679                ctx,
680                BufferType::IndexBuffer,
681                MAX_INDICES * std::mem::size_of::<u16>(),
682            );
683            let bindings = Bindings {
684                vertex_buffers: vec![vertex_buffer],
685                index_buffer,
686                images: vec![Texture::empty(), Texture::empty()],
687            };
688
689            self.draw_calls_bindings.push(bindings);
690        }
691        assert_eq!(self.draw_calls_bindings.len(), self.draw_calls.len());
692
693        let (screen_width, screen_height) = ctx.screen_size();
694        let time = (miniquad::date::now() - self.start_time) as f32;
695        let time = glam::vec4(time, time.sin(), time.cos(), 0.);
696
697        for (dc, bindings) in self.draw_calls[0..self.draw_calls_count]
698            .iter_mut()
699            .zip(self.draw_calls_bindings.iter_mut())
700        {
701            let pipeline = self.pipelines.get_quad_pipeline(dc.pipeline);
702
703            let (width, height) = if let Some(render_pass) = dc.render_pass {
704                let render_texture = render_pass.texture(ctx);
705
706                (render_texture.width, render_texture.height)
707            } else {
708                (screen_width as u32, screen_height as u32)
709            };
710
711            if pipeline.wants_screen_texture {
712                self.state.snapshoter.snapshot(ctx, dc.render_pass);
713            }
714
715            if let Some(render_pass) = dc.render_pass {
716                ctx.begin_pass(render_pass, PassAction::Nothing);
717            } else {
718                ctx.begin_default_pass(PassAction::Nothing);
719            }
720
721            bindings.vertex_buffers[0].update(ctx, dc.vertices());
722            bindings.index_buffer.update(ctx, dc.indices());
723
724            bindings.images[0] = dc.texture;
725            bindings.images[1] = self.state.snapshoter.screen_texture.map_or_else(
726                || Texture::empty(),
727                |texture| texture.raw_miniquad_texture_handle(),
728            );
729
730            ctx.apply_pipeline(&pipeline.pipeline);
731            if let Some(clip) = dc.clip {
732                ctx.apply_scissor_rect(clip.0, height as i32 - (clip.1 + clip.3), clip.2, clip.3);
733            } else {
734                ctx.apply_scissor_rect(0, 0, width as i32, height as i32);
735            }
736            ctx.apply_bindings(&bindings);
737
738            ctx.apply_uniforms(&shader::Uniforms {
739                projection: dc.projection,
740                model: dc.model,
741                time,
742                data: pipeline.uniforms_data.clone(),
743            });
744            ctx.draw(0, dc.indices_count as i32, 1);
745
746            dc.vertices_count = 0;
747            dc.indices_count = 0;
748
749            ctx.end_render_pass();
750        }
751
752        self.draw_calls_count = 0;
753    }
754
755    pub fn get_projection_matrix(&self) -> glam::Mat4 {
756        self.state.projection
757    }
758
759    pub fn get_active_render_pass(&self) -> Option<RenderPass> {
760        self.state.render_pass
761    }
762
763    pub fn render_pass(&mut self, render_pass: Option<RenderPass>) {
764        self.state.render_pass = render_pass;
765    }
766
767    pub fn depth_test(&mut self, enable: bool) {
768        self.state.depth_test_enable = enable;
769    }
770
771    pub fn texture(&mut self, texture: Option<Texture2D>) {
772        self.state.texture = texture.map_or(self.white_texture, |t| t.texture);
773    }
774
775    pub fn scissor(&mut self, clip: Option<(i32, i32, i32, i32)>) {
776        self.state.clip = clip;
777    }
778
779    pub fn set_projection_matrix(&mut self, matrix: glam::Mat4) {
780        self.state.projection = matrix;
781    }
782
783    pub fn push_model_matrix(&mut self, matrix: glam::Mat4) {
784        self.state.model_stack.push(self.state.model() * matrix);
785    }
786
787    pub fn pop_model_matrix(&mut self) {
788        if self.state.model_stack.len() > 1 {
789            self.state.model_stack.pop();
790        }
791    }
792
793    pub fn pipeline(&mut self, pipeline: Option<GlPipeline>) {
794        self.state.pipeline = pipeline;
795    }
796
797    pub fn draw_mode(&mut self, mode: DrawMode) {
798        self.state.draw_mode = mode;
799    }
800
801    pub fn geometry(&mut self, vertices: &[impl Into<VertexInterop> + Copy], indices: &[u16]) {
802        let pip = self.state.pipeline.unwrap_or(
803            self.pipelines
804                .get(self.state.draw_mode, self.state.depth_test_enable),
805        );
806
807        let previous_dc_ix = if self.draw_calls_count == 0 {
808            None
809        } else {
810            Some(self.draw_calls_count - 1)
811        };
812        let previous_dc = previous_dc_ix.and_then(|ix| self.draw_calls.get(ix));
813
814        if previous_dc.map_or(true, |draw_call| {
815            draw_call.texture != self.state.texture
816                || draw_call.clip != self.state.clip
817                || draw_call.model != self.state.model()
818                || draw_call.pipeline != pip
819                || draw_call.render_pass != self.state.render_pass
820                || draw_call.draw_mode != self.state.draw_mode
821                || draw_call.projection != self.state.projection
822                || draw_call.vertices_count >= MAX_VERTICES - vertices.len()
823                || draw_call.indices_count >= MAX_INDICES - indices.len()
824        }) {
825            if self.draw_calls_count >= self.draw_calls.len() {
826                self.draw_calls.push(DrawCall::new(
827                    self.state.texture,
828                    self.state.projection,
829                    self.state.model(),
830                    self.state.draw_mode,
831                    pip,
832                    self.state.render_pass,
833                ));
834            }
835            self.draw_calls[self.draw_calls_count].texture = self.state.texture;
836            self.draw_calls[self.draw_calls_count].vertices_count = 0;
837            self.draw_calls[self.draw_calls_count].indices_count = 0;
838            self.draw_calls[self.draw_calls_count].clip = self.state.clip;
839            self.draw_calls[self.draw_calls_count].projection = self.state.projection;
840            self.draw_calls[self.draw_calls_count].model = self.state.model();
841            self.draw_calls[self.draw_calls_count].pipeline = pip;
842            self.draw_calls[self.draw_calls_count].render_pass = self.state.render_pass;
843
844            self.draw_calls_count += 1;
845        };
846        let dc = &mut self.draw_calls[self.draw_calls_count - 1];
847
848        for i in 0..vertices.len() {
849            dc.vertices[dc.vertices_count + i] = vertices[i].into().into();
850        }
851
852        for i in 0..indices.len() {
853            dc.indices[dc.indices_count + i] = indices[i] + dc.vertices_count as u16;
854        }
855        dc.vertices_count += vertices.len();
856        dc.indices_count += indices.len();
857        dc.texture = self.state.texture;
858    }
859
860    pub fn delete_pipeline(&mut self, pipeline: GlPipeline) {
861        self.pipelines.delete_pipeline(pipeline);
862    }
863
864    pub fn set_uniform<T>(&mut self, pipeline: GlPipeline, name: &str, uniform: T) {
865        let pipeline = self.pipelines.get_quad_pipeline_mut(pipeline);
866
867        let uniform_meta = pipeline.uniforms.iter().find(
868            |Uniform {
869                 name: uniform_name, ..
870             }| uniform_name == name,
871        );
872        if uniform_meta.is_none() {
873            println!("Trying to set non-existing uniform: {}", name);
874            return;
875        }
876        let uniform_meta = uniform_meta.unwrap();
877        let uniform_format = uniform_meta.uniform_type;
878        let uniform_byte_size = uniform_format.size();
879        let uniform_byte_offset = uniform_meta.byte_offset;
880
881        if std::mem::size_of::<T>() != uniform_byte_size {
882            println!(
883                "Trying to set uniform {} sized {} bytes value of {} bytes",
884                name,
885                std::mem::size_of::<T>(),
886                uniform_byte_size
887            );
888            return;
889        }
890        macro_rules! transmute_uniform {
891            ($uniform_size:expr, $byte_offset:expr, $n:expr) => {
892                if $uniform_size == $n {
893                    let data: [u8; $n] = unsafe { std::mem::transmute_copy(&uniform) };
894
895                    for i in 0..$uniform_size {
896                        pipeline.uniforms_data[$byte_offset + i] = data[i];
897                    }
898                }
899            };
900        }
901        transmute_uniform!(uniform_byte_size, uniform_byte_offset, 4);
902        transmute_uniform!(uniform_byte_size, uniform_byte_offset, 8);
903        transmute_uniform!(uniform_byte_size, uniform_byte_offset, 12);
904        transmute_uniform!(uniform_byte_size, uniform_byte_offset, 16);
905        transmute_uniform!(uniform_byte_size, uniform_byte_offset, 64);
906    }
907}
908
909/// Texture, data stored in GPU memory
910#[derive(Clone, Copy, Debug)]
911pub struct Texture2D {
912    texture: miniquad::Texture,
913}
914
915impl Texture2D {
916    pub fn from_miniquad_texture(texture: miniquad::Texture) -> Texture2D {
917        Texture2D { texture }
918    }
919
920    pub fn empty() -> Texture2D {
921        Texture2D {
922            texture: miniquad::Texture::empty(),
923        }
924    }
925
926    pub fn update(&mut self, ctx: &mut miniquad::Context, image: &Image) {
927        assert_eq!(self.texture.width, image.width as u32);
928        assert_eq!(self.texture.height, image.height as u32);
929
930        self.texture.update(ctx, &image.bytes);
931    }
932
933    pub fn width(&self) -> f32 {
934        self.texture.width as f32
935    }
936
937    pub fn height(&self) -> f32 {
938        self.texture.height as f32
939    }
940
941    pub fn from_file_with_format<'a>(
942        ctx: &mut miniquad::Context,
943        bytes: &[u8],
944        format: Option<image::ImageFormat>,
945    ) -> Texture2D {
946        let img = if let Some(fmt) = format {
947            image::load_from_memory_with_format(&bytes, fmt)
948                .unwrap_or_else(|e| panic!("{}", e))
949                .to_rgba()
950        } else {
951            image::load_from_memory(&bytes)
952                .unwrap_or_else(|e| panic!("{}", e))
953                .to_rgba()
954        };
955        let width = img.width() as u16;
956        let height = img.height() as u16;
957        let bytes = img.into_raw();
958
959        Self::from_rgba8(ctx, width, height, &bytes)
960    }
961
962    pub fn from_rgba8(
963        ctx: &mut miniquad::Context,
964        width: u16,
965        height: u16,
966        bytes: &[u8],
967    ) -> Texture2D {
968        let texture = miniquad::Texture::from_rgba8(ctx, width, height, &bytes);
969
970        Texture2D { texture }
971    }
972
973    pub fn set_filter(&self, ctx: &mut miniquad::Context, filter_mode: FilterMode) {
974        self.texture.set_filter(ctx, filter_mode);
975    }
976
977    pub fn raw_miniquad_texture_handle(&self) -> Texture {
978        self.texture
979    }
980
981    pub fn grab_screen(&self) {
982        let (internal_format, _, _) = self.texture.format.into();
983        unsafe {
984            gl::glBindTexture(gl::GL_TEXTURE_2D, self.texture.gl_internal_id());
985            gl::glCopyTexImage2D(
986                gl::GL_TEXTURE_2D,
987                0,
988                internal_format,
989                0,
990                0,
991                self.texture.width as _,
992                self.texture.height as _,
993                0,
994            );
995        }
996    }
997
998    pub fn get_texture_data(&self) -> Image {
999        let mut image = Image {
1000            width: self.texture.width as _,
1001            height: self.texture.height as _,
1002            bytes: vec![0; self.texture.width as usize * self.texture.height as usize * 4],
1003        };
1004
1005        self.texture.read_pixels(&mut image.bytes);
1006
1007        image
1008    }
1009}
1010
1011/// Image, data stored in CPU memory
1012pub struct Image {
1013    pub bytes: Vec<u8>,
1014    pub width: u16,
1015    pub height: u16,
1016}
1017
1018impl Image {
1019    pub fn from_file_with_format(bytes: &[u8], format: Option<image::ImageFormat>) -> Image {
1020        let img = if let Some(fmt) = format {
1021            image::load_from_memory_with_format(&bytes, fmt)
1022                .unwrap_or_else(|e| panic!("{}", e))
1023                .to_rgba()
1024        } else {
1025            image::load_from_memory(&bytes)
1026                .unwrap_or_else(|e| panic!("{}", e))
1027                .to_rgba()
1028        };
1029        let width = img.width() as u16;
1030        let height = img.height() as u16;
1031        let bytes = img.into_raw();
1032
1033        Image {
1034            width,
1035            height,
1036            bytes,
1037        }
1038    }
1039
1040    pub fn empty() -> Image {
1041        Image {
1042            width: 0,
1043            height: 0,
1044            bytes: vec![],
1045        }
1046    }
1047
1048    pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image {
1049        let mut bytes = vec![0; width as usize * height as usize * 4];
1050        for i in 0..width as usize * height as usize {
1051            bytes[i * 4 + 0] = (color.r * 255.) as u8;
1052            bytes[i * 4 + 1] = (color.g * 255.) as u8;
1053            bytes[i * 4 + 2] = (color.b * 255.) as u8;
1054            bytes[i * 4 + 3] = (color.a * 255.) as u8;
1055        }
1056        Image {
1057            width,
1058            height,
1059            bytes,
1060        }
1061    }
1062
1063    pub fn update(&mut self, colors: &[Color]) {
1064        assert!(self.width as usize * self.height as usize == colors.len());
1065
1066        for i in 0..colors.len() {
1067            self.bytes[i * 4] = (colors[i].r * 255.) as u8;
1068            self.bytes[i * 4 + 1] = (colors[i].g * 255.) as u8;
1069            self.bytes[i * 4 + 2] = (colors[i].b * 255.) as u8;
1070            self.bytes[i * 4 + 3] = (colors[i].a * 255.) as u8;
1071        }
1072    }
1073    pub fn width(&self) -> usize {
1074        self.width as usize
1075    }
1076
1077    pub fn height(&self) -> usize {
1078        self.height as usize
1079    }
1080
1081    pub fn get_image_data(&self) -> &[[u8; 4]] {
1082        use std::slice;
1083
1084        unsafe {
1085            slice::from_raw_parts(
1086                self.bytes.as_ptr() as *const [u8; 4],
1087                self.width as usize * self.height as usize,
1088            )
1089        }
1090    }
1091
1092    pub fn get_image_data_mut(&mut self) -> &mut [[u8; 4]] {
1093        use std::slice;
1094
1095        unsafe {
1096            slice::from_raw_parts_mut(
1097                self.bytes.as_mut_ptr() as *mut [u8; 4],
1098                self.width as usize * self.height as usize,
1099            )
1100        }
1101    }
1102
1103    pub fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
1104        let width = self.width;
1105
1106        self.get_image_data_mut()[(y * width as u32 + x) as usize] = color.into();
1107    }
1108
1109    pub fn get_pixel(&self, x: u32, y: u32) -> Color {
1110        self.get_image_data()[(y * self.width as u32 + x) as usize].into()
1111    }
1112
1113    pub fn export_png(&self, path: &str) {
1114        let mut bytes = vec![0; self.width as usize * self.height as usize * 4];
1115
1116        // flip the image before saving
1117        for y in 0..self.height as usize {
1118            for x in 0..self.width as usize * 4 {
1119                bytes[y * self.width as usize * 4 + x] =
1120                    self.bytes[(self.height as usize - y - 1) * self.width as usize * 4 + x];
1121            }
1122        }
1123
1124        image::save_buffer(
1125            path,
1126            &bytes[..],
1127            self.width as _,
1128            self.height as _,
1129            image::ColorType::RGBA(8),
1130        )
1131        .unwrap();
1132    }
1133}
1134
1135mod shader {
1136    use super::UNIFORMS_ARRAY_SIZE;
1137    use miniquad::{ShaderMeta, UniformBlockLayout, UniformDesc, UniformType};
1138
1139    pub const VERTEX: &str = r#"#version 100
1140    attribute vec3 position;
1141    attribute vec2 texcoord;
1142    attribute vec4 color0;
1143
1144    varying lowp vec2 uv;
1145    varying lowp vec4 color;
1146
1147    uniform mat4 Model;
1148    uniform mat4 Projection;
1149
1150    void main() {
1151        gl_Position = Projection * Model * vec4(position, 1);
1152        color = color0 / 255.0;
1153        uv = texcoord;
1154    }"#;
1155
1156    pub const FRAGMENT: &str = r#"#version 100
1157    varying lowp vec4 color;
1158    varying lowp vec2 uv;
1159    
1160    uniform sampler2D Texture;
1161
1162    void main() {
1163        gl_FragColor = color * texture2D(Texture, uv) ;
1164    }"#;
1165
1166    pub fn meta() -> ShaderMeta {
1167        ShaderMeta {
1168            images: vec!["Texture".to_string(), "_ScreenTexture".to_string()],
1169            uniforms: UniformBlockLayout {
1170                uniforms: vec![
1171                    UniformDesc::new("Projection", UniformType::Mat4),
1172                    UniformDesc::new("Model", UniformType::Mat4),
1173                    UniformDesc::new("_Time", UniformType::Float4),
1174                ],
1175            },
1176        }
1177    }
1178
1179    #[repr(C)]
1180    pub struct Uniforms {
1181        pub projection: glam::Mat4,
1182        pub model: glam::Mat4,
1183        pub time: glam::Vec4,
1184
1185        pub data: [u8; UNIFORMS_ARRAY_SIZE],
1186    }
1187}