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#[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 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 pub fn clear_draw_calls(&mut self) {
658 self.draw_calls_count = 0;
659 }
660
661 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#[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
1011pub 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 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}