use miniquad::*;
pub use colors::*;
pub use miniquad::{FilterMode, ShaderError};
use crate::texture::Texture2D;
use std::collections::BTreeMap;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
#[macro_export]
macro_rules! color_u8 {
($r:expr, $g:expr, $b:expr, $a:expr) => {
Color::new(
$r as f32 / 255.,
$g as f32 / 255.,
$b as f32 / 255.,
$a as f32 / 255.,
)
};
}
#[test]
fn color_from_bytes() {
assert_eq!(Color::new(1.0, 0.0, 0.0, 1.0), color_u8!(255, 0, 0, 255));
assert_eq!(
Color::new(1.0, 0.5, 0.0, 1.0),
color_u8!(255, 127.5, 0, 255)
);
assert_eq!(
Color::new(0.0, 1.0, 0.5, 1.0),
color_u8!(0, 255, 127.5, 255)
);
}
impl Into<[u8; 4]> for Color {
fn into(self) -> [u8; 4] {
[
(self.r * 255.) as u8,
(self.g * 255.) as u8,
(self.b * 255.) as u8,
(self.a * 255.) as u8,
]
}
}
impl Into<Color> for [u8; 4] {
fn into(self) -> Color {
Color::new(
self[0] as f32 / 255.,
self[1] as f32 / 255.,
self[2] as f32 / 255.,
self[3] as f32 / 255.,
)
}
}
impl Into<[f32; 4]> for Color {
fn into(self) -> [f32; 4] {
[self.r, self.g, self.b, self.a]
}
}
impl From<[f32; 4]> for Color {
fn from(colors: [f32; 4]) -> Color {
Color::new(colors[0], colors[1], colors[2], colors[3])
}
}
impl Color {
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
Color { r, g, b, a }
}
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
Color::new(
r as f32 / 255.,
g as f32 / 255.,
b as f32 / 255.,
a as f32 / 255.,
)
}
pub fn to_vec(&self) -> glam::Vec4 {
glam::Vec4::new(self.r, self.g, self.b, self.a)
}
pub fn from_vec(vec: glam::Vec4) -> Self {
Self::new(vec.x, vec.y, vec.z, vec.w)
}
}
pub mod colors {
use super::Color;
pub const LIGHTGRAY: Color = Color::new(0.78, 0.78, 0.78, 1.00);
pub const GRAY: Color = Color::new(0.51, 0.51, 0.51, 1.00);
pub const DARKGRAY: Color = Color::new(0.31, 0.31, 0.31, 1.00);
pub const YELLOW: Color = Color::new(0.99, 0.98, 0.00, 1.00);
pub const GOLD: Color = Color::new(1.00, 0.80, 0.00, 1.00);
pub const ORANGE: Color = Color::new(1.00, 0.63, 0.00, 1.00);
pub const PINK: Color = Color::new(1.00, 0.43, 0.76, 1.00);
pub const RED: Color = Color::new(0.90, 0.16, 0.22, 1.00);
pub const MAROON: Color = Color::new(0.75, 0.13, 0.22, 1.00);
pub const GREEN: Color = Color::new(0.00, 0.89, 0.19, 1.00);
pub const LIME: Color = Color::new(0.00, 0.62, 0.18, 1.00);
pub const DARKGREEN: Color = Color::new(0.00, 0.46, 0.17, 1.00);
pub const SKYBLUE: Color = Color::new(0.40, 0.75, 1.00, 1.00);
pub const BLUE: Color = Color::new(0.00, 0.47, 0.95, 1.00);
pub const DARKBLUE: Color = Color::new(0.00, 0.32, 0.67, 1.00);
pub const PURPLE: Color = Color::new(0.78, 0.48, 1.00, 1.00);
pub const VIOLET: Color = Color::new(0.53, 0.24, 0.75, 1.00);
pub const DARKPURPLE: Color = Color::new(0.44, 0.12, 0.49, 1.00);
pub const BEIGE: Color = Color::new(0.83, 0.69, 0.51, 1.00);
pub const BROWN: Color = Color::new(0.50, 0.42, 0.31, 1.00);
pub const DARKBROWN: Color = Color::new(0.30, 0.25, 0.18, 1.00);
pub const WHITE: Color = Color::new(1.00, 1.00, 1.00, 1.00);
pub const BLACK: Color = Color::new(0.00, 0.00, 0.00, 1.00);
pub const BLANK: Color = Color::new(0.00, 0.00, 0.00, 0.00);
pub const MAGENTA: Color = Color::new(1.00, 0.00, 1.00, 1.00);
}
const MAX_VERTICES: usize = 10000;
const MAX_INDICES: usize = 5000;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DrawMode {
Triangles,
Lines,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GlPipeline(usize);
struct DrawCall {
vertices: [Vertex; MAX_VERTICES],
indices: [u16; MAX_INDICES],
vertices_count: usize,
indices_count: usize,
clip: Option<(i32, i32, i32, i32)>,
texture: Texture,
model: glam::Mat4,
draw_mode: DrawMode,
pipeline: GlPipeline,
render_pass: Option<RenderPass>,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct Vertex {
pos: [f32; 3],
uv: [f32; 2],
color: [u8; 4],
}
pub type VertexInterop = ([f32; 3], [f32; 2], [f32; 4]);
impl Into<VertexInterop> for Vertex {
fn into(self) -> VertexInterop {
(
self.pos,
self.uv,
[
self.color[0] as f32 / 255.0,
self.color[1] as f32 / 255.0,
self.color[2] as f32 / 255.0,
self.color[3] as f32 / 255.0,
],
)
}
}
impl Into<Vertex> for VertexInterop {
fn into(self) -> Vertex {
Vertex {
pos: self.0,
uv: self.1,
color: [
((self.2)[0] * 255.) as u8,
((self.2)[1] * 255.) as u8,
((self.2)[2] * 255.) as u8,
((self.2)[3] * 255.) as u8,
],
}
}
}
impl Vertex {
pub fn new(x: f32, y: f32, z: f32, u: f32, v: f32, color: Color) -> Vertex {
Vertex {
pos: [x, y, z],
uv: [u, v],
color: [
(color.r * 255.) as u8,
(color.g * 255.) as u8,
(color.b * 255.) as u8,
(color.a * 255.) as u8,
],
}
}
}
impl DrawCall {
fn new(
texture: Texture,
model: glam::Mat4,
draw_mode: DrawMode,
pipeline: GlPipeline,
render_pass: Option<RenderPass>,
) -> DrawCall {
DrawCall {
vertices: [Vertex::new(0., 0., 0., 0., 0., Color::new(0.0, 0.0, 0.0, 0.0));
MAX_VERTICES],
indices: [0; MAX_INDICES],
vertices_count: 0,
indices_count: 0,
clip: None,
texture,
model,
draw_mode,
pipeline,
render_pass,
}
}
fn vertices(&self) -> &[Vertex] {
&self.vertices[0..self.vertices_count]
}
fn indices(&self) -> &[u16] {
&self.indices[0..self.indices_count]
}
}
struct MagicSnapshotter {
pipeline: Pipeline,
bindings: Bindings,
pass: Option<RenderPass>,
screen_texture: Option<Texture2D>,
}
mod snapshotter_shader {
use miniquad::{ShaderMeta, UniformBlockLayout};
pub const VERTEX: &str = r#"#version 100
attribute vec2 position;
attribute vec2 texcoord;
varying lowp vec2 uv;
void main() {
gl_Position = vec4(position, 0, 1);
uv = texcoord;
}"#;
pub const FRAGMENT: &str = r#"#version 100
varying lowp vec2 uv;
uniform sampler2D Texture;
void main() {
gl_FragColor = texture2D(Texture, uv);
}"#;
pub fn meta() -> ShaderMeta {
ShaderMeta {
images: vec!["Texture".to_string()],
uniforms: UniformBlockLayout { uniforms: vec![] },
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Uniforms {}
}
impl MagicSnapshotter {
fn new(ctx: &mut Context) -> MagicSnapshotter {
let shader = Shader::new(
ctx,
snapshotter_shader::VERTEX,
snapshotter_shader::FRAGMENT,
snapshotter_shader::meta(),
)
.unwrap_or_else(|e| panic!("Failed to load shader: {}", e));
let pipeline = Pipeline::with_params(
ctx,
&[BufferLayout::default()],
&[
VertexAttribute::new("position", VertexFormat::Float2),
VertexAttribute::new("texcoord", VertexFormat::Float2),
],
shader,
PipelineParams::default(),
);
#[rustfmt::skip]
let vertices: [f32; 16] = [
-1.0, -1.0, 0., 0.,
1.0, -1.0, 1., 0. ,
1.0, 1.0, 1., 1. ,
-1.0, 1.0, 0., 1. ,
];
let vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, &vertices);
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
let index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, &indices);
let bindings = Bindings {
vertex_buffers: vec![vertex_buffer],
index_buffer,
images: vec![Texture::empty()],
};
MagicSnapshotter {
pipeline,
bindings,
pass: None,
screen_texture: None,
}
}
fn snapshot(&mut self, ctx: &mut Context, camera_render_pass: Option<RenderPass>) {
if let Some(camera_render_pass) = camera_render_pass {
let texture = camera_render_pass.texture(ctx);
if self.pass.is_none() {
let color_img = Texture::new_render_texture(
ctx,
TextureParams {
width: texture.width,
height: texture.height,
format: texture.format,
..Default::default()
},
);
self.pass = Some(RenderPass::new(ctx, color_img, None));
self.screen_texture = Some(Texture2D::from_miniquad_texture(color_img));
}
if self.bindings.images.len() == 0 {
self.bindings.images.push(texture);
} else {
self.bindings.images[0] = texture;
}
ctx.begin_pass(
self.pass.unwrap(),
PassAction::clear_color(1.0, 0.0, 1.0, 1.),
);
ctx.apply_pipeline(&self.pipeline);
ctx.apply_bindings(&self.bindings);
ctx.draw(0, 6, 1);
ctx.end_render_pass();
} else {
let (screen_width, screen_height) = ctx.screen_size();
if self.screen_texture.is_none()
|| self
.screen_texture
.map(|t| {
t.texture.width != screen_width as _
|| t.texture.height != screen_height as _
})
.unwrap_or(false)
{
self.screen_texture = Some(Texture2D::from_miniquad_texture(
Texture::new_render_texture(
ctx,
TextureParams {
width: screen_width as _,
height: screen_height as _,
..Default::default()
},
),
))
}
let texture = self.screen_texture.unwrap();
texture.grab_screen();
}
}
}
struct GlState {
texture: Texture,
draw_mode: DrawMode,
clip: Option<(i32, i32, i32, i32)>,
model_stack: Vec<glam::Mat4>,
pipeline: Option<GlPipeline>,
depth_test_enable: bool,
snapshotter: MagicSnapshotter,
render_pass: Option<RenderPass>,
}
impl GlState {
fn model(&self) -> glam::Mat4 {
*self.model_stack.last().unwrap()
}
}
#[derive(Clone)]
struct Uniform {
name: String,
uniform_type: UniformType,
byte_offset: usize,
}
#[derive(Clone)]
struct PipelineExt {
pipeline: miniquad::Pipeline,
wants_screen_texture: bool,
uniforms: Vec<Uniform>,
uniforms_data: Vec<u8>,
textures: Vec<String>,
textures_data: BTreeMap<String, Texture>,
}
impl PipelineExt {
fn set_uniform<T>(&mut self, name: &str, uniform: T) {
let uniform_meta = self.uniforms.iter().find(
|Uniform {
name: uniform_name, ..
}| uniform_name == name,
);
if uniform_meta.is_none() {
println!("Trying to set non-existing uniform: {}", name);
return;
}
let uniform_meta = uniform_meta.unwrap();
let uniform_format = uniform_meta.uniform_type;
let uniform_byte_size = uniform_format.size();
let uniform_byte_offset = uniform_meta.byte_offset;
if std::mem::size_of::<T>() != uniform_byte_size {
println!(
"Trying to set uniform {} sized {} bytes value of {} bytes",
name,
std::mem::size_of::<T>(),
uniform_byte_size
);
return;
}
macro_rules! transmute_uniform {
($uniform_size:expr, $byte_offset:expr, $n:expr) => {
if $uniform_size == $n {
let data: [u8; $n] = unsafe { std::mem::transmute_copy(&uniform) };
for i in 0..$uniform_size {
self.uniforms_data[$byte_offset + i] = data[i];
}
}
};
}
transmute_uniform!(uniform_byte_size, uniform_byte_offset, 4);
transmute_uniform!(uniform_byte_size, uniform_byte_offset, 8);
transmute_uniform!(uniform_byte_size, uniform_byte_offset, 12);
transmute_uniform!(uniform_byte_size, uniform_byte_offset, 16);
transmute_uniform!(uniform_byte_size, uniform_byte_offset, 64);
}
}
struct PipelinesStorage {
pipelines: [Option<PipelineExt>; Self::MAX_PIPELINES],
pipelines_amount: usize,
}
impl PipelinesStorage {
const MAX_PIPELINES: usize = 32;
const TRIANGLES_PIPELINE: GlPipeline = GlPipeline(0);
const LINES_PIPELINE: GlPipeline = GlPipeline(1);
const TRIANGLES_DEPTH_PIPELINE: GlPipeline = GlPipeline(2);
const LINES_DEPTH_PIPELINE: GlPipeline = GlPipeline(3);
fn new(ctx: &mut miniquad::Context) -> PipelinesStorage {
let shader = Shader::new(ctx, shader::VERTEX, shader::FRAGMENT, shader::meta())
.unwrap_or_else(|e| panic!("Failed to load shader: {}", e));
let params = PipelineParams {
color_blend: Some(BlendState::new(
Equation::Add,
BlendFactor::Value(BlendValue::SourceAlpha),
BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
)),
..Default::default()
};
let mut storage = PipelinesStorage {
pipelines: Default::default(),
pipelines_amount: 0,
};
let triangles_pipeline = storage.make_pipeline(
ctx,
shader,
PipelineParams {
primitive_type: PrimitiveType::Triangles,
..params
},
false,
vec![],
vec![],
);
assert_eq!(triangles_pipeline, Self::TRIANGLES_PIPELINE);
let lines_pipeline = storage.make_pipeline(
ctx,
shader,
PipelineParams {
primitive_type: PrimitiveType::Lines,
..params
},
false,
vec![],
vec![],
);
assert_eq!(lines_pipeline, Self::LINES_PIPELINE);
let triangles_depth_pipeline = storage.make_pipeline(
ctx,
shader,
PipelineParams {
depth_write: true,
depth_test: Comparison::LessOrEqual,
primitive_type: PrimitiveType::Triangles,
..params
},
false,
vec![],
vec![],
);
assert_eq!(triangles_depth_pipeline, Self::TRIANGLES_DEPTH_PIPELINE);
let lines_depth_pipeline = storage.make_pipeline(
ctx,
shader,
PipelineParams {
depth_write: true,
depth_test: Comparison::LessOrEqual,
primitive_type: PrimitiveType::Lines,
..params
},
false,
vec![],
vec![],
);
assert_eq!(lines_depth_pipeline, Self::LINES_DEPTH_PIPELINE);
storage
}
fn make_pipeline(
&mut self,
ctx: &mut Context,
shader: Shader,
params: PipelineParams,
wants_screen_texture: bool,
mut uniforms: Vec<(String, UniformType)>,
textures: Vec<String>,
) -> GlPipeline {
let pipeline = Pipeline::with_params(
ctx,
&[BufferLayout::default()],
&[
VertexAttribute::new("position", VertexFormat::Float3),
VertexAttribute::new("texcoord", VertexFormat::Float2),
VertexAttribute::new("color0", VertexFormat::Byte4),
],
shader,
params,
);
let id = self
.pipelines
.iter()
.position(|p| p.is_none())
.unwrap_or_else(|| panic!("Pipelines amount exceeded"));
let mut max_offset = 0;
for (name, kind) in shader::uniforms().into_iter().rev() {
uniforms.insert(0, (name.to_owned(), kind));
}
let uniforms = uniforms
.iter()
.scan(0, |offset, uniform| {
let uniform_byte_size = uniform.1.size();
let uniform = Uniform {
name: uniform.0.clone(),
uniform_type: uniform.1,
byte_offset: *offset,
};
*offset += uniform_byte_size;
max_offset = *offset;
Some(uniform)
})
.collect();
self.pipelines[id] = Some(PipelineExt {
pipeline,
wants_screen_texture,
uniforms,
uniforms_data: vec![0; max_offset],
textures,
textures_data: BTreeMap::new(),
});
self.pipelines_amount += 1;
GlPipeline(id)
}
fn get(&self, draw_mode: DrawMode, depth_enabled: bool) -> GlPipeline {
match (draw_mode, depth_enabled) {
(DrawMode::Triangles, false) => Self::TRIANGLES_PIPELINE,
(DrawMode::Triangles, true) => Self::TRIANGLES_DEPTH_PIPELINE,
(DrawMode::Lines, false) => Self::LINES_PIPELINE,
(DrawMode::Lines, true) => Self::LINES_DEPTH_PIPELINE,
}
}
fn get_quad_pipeline_mut(&mut self, pip: GlPipeline) -> &mut PipelineExt {
self.pipelines[pip.0].as_mut().unwrap()
}
fn delete_pipeline(&mut self, pip: GlPipeline) {
self.pipelines[pip.0] = None;
}
}
pub struct QuadGl {
pipelines: PipelinesStorage,
draw_calls: Vec<DrawCall>,
draw_calls_bindings: Vec<Bindings>,
draw_calls_count: usize,
state: GlState,
start_time: f64,
white_texture: Texture,
}
impl QuadGl {
pub fn new(ctx: &mut miniquad::Context) -> QuadGl {
let white_texture = Texture::from_rgba8(ctx, 1, 1, &[255, 255, 255, 255]);
QuadGl {
pipelines: PipelinesStorage::new(ctx),
state: GlState {
clip: None,
texture: white_texture,
model_stack: vec![glam::Mat4::IDENTITY],
draw_mode: DrawMode::Triangles,
pipeline: None,
depth_test_enable: false,
snapshotter: MagicSnapshotter::new(ctx),
render_pass: None,
},
draw_calls: Vec::with_capacity(200),
draw_calls_bindings: Vec::with_capacity(200),
draw_calls_count: 0,
start_time: miniquad::date::now(),
white_texture,
}
}
pub fn make_pipeline(
&mut self,
ctx: &mut Context,
vertex_shader: &str,
fragment_shader: &str,
params: PipelineParams,
uniforms: Vec<(String, UniformType)>,
textures: Vec<String>,
) -> Result<GlPipeline, ShaderError> {
let mut shader_meta: ShaderMeta = shader::meta();
for uniform in &uniforms {
shader_meta
.uniforms
.uniforms
.push(UniformDesc::new(&uniform.0, uniform.1));
}
for texture in &textures {
if texture == "Texture" {
panic!(
"you can't use name `Texture` for your texture. This name is reserved for the texture that will be drawn with that material"
);
}
if texture == "_ScreenTexture" {
panic!(
"you can't use name `_ScreenTexture` for your texture in shaders. This name is reserved for screen texture"
);
}
shader_meta.images.push(texture.clone());
}
let shader = Shader::new(ctx, vertex_shader, fragment_shader, shader_meta)?;
let wants_screen_texture = fragment_shader.find("_ScreenTexture").is_some();
Ok(self.pipelines.make_pipeline(
ctx,
shader,
params,
wants_screen_texture,
uniforms,
textures,
))
}
pub fn clear_draw_calls(&mut self) {
self.draw_calls_count = 0;
}
pub fn reset(&mut self) {
self.state.clip = None;
self.state.texture = self.white_texture;
self.state.model_stack = vec![glam::Mat4::IDENTITY];
self.draw_calls_count = 0;
}
pub fn draw(&mut self, ctx: &mut miniquad::Context, projection: glam::Mat4) {
for _ in 0..self.draw_calls.len() - self.draw_calls_bindings.len() {
let vertex_buffer = Buffer::stream(
ctx,
BufferType::VertexBuffer,
MAX_VERTICES * std::mem::size_of::<Vertex>(),
);
let index_buffer = Buffer::stream(
ctx,
BufferType::IndexBuffer,
MAX_INDICES * std::mem::size_of::<u16>(),
);
let bindings = Bindings {
vertex_buffers: vec![vertex_buffer],
index_buffer,
images: vec![Texture::empty(), Texture::empty()],
};
self.draw_calls_bindings.push(bindings);
}
assert_eq!(self.draw_calls_bindings.len(), self.draw_calls.len());
let (screen_width, screen_height) = ctx.screen_size();
let time = (miniquad::date::now() - self.start_time) as f32;
let time = glam::vec4(time, time.sin(), time.cos(), 0.);
for (dc, bindings) in self.draw_calls[0..self.draw_calls_count]
.iter_mut()
.zip(self.draw_calls_bindings.iter_mut())
{
let pipeline = self.pipelines.get_quad_pipeline_mut(dc.pipeline);
let (width, height) = if let Some(render_pass) = dc.render_pass {
let render_texture = render_pass.texture(ctx);
(render_texture.width, render_texture.height)
} else {
(screen_width as u32, screen_height as u32)
};
if pipeline.wants_screen_texture {
self.state.snapshotter.snapshot(ctx, dc.render_pass);
}
if let Some(render_pass) = dc.render_pass {
ctx.begin_pass(render_pass, PassAction::Nothing);
} else {
ctx.begin_default_pass(PassAction::Nothing);
}
bindings.vertex_buffers[0].update(ctx, dc.vertices());
bindings.index_buffer.update(ctx, dc.indices());
bindings.images[0] = dc.texture;
bindings.images[1] = self.state.snapshotter.screen_texture.map_or_else(
|| Texture::empty(),
|texture| texture.raw_miniquad_texture_handle(),
);
bindings
.images
.resize(2 + pipeline.textures.len(), Texture::empty());
for (pos, name) in pipeline.textures.iter().enumerate() {
if let Some(texture) = pipeline.textures_data.get(name).copied() {
bindings.images[2 + pos] = texture;
}
}
ctx.apply_pipeline(&pipeline.pipeline);
if let Some(clip) = dc.clip {
ctx.apply_scissor_rect(clip.0, height as i32 - (clip.1 + clip.3), clip.2, clip.3);
} else {
ctx.apply_scissor_rect(0, 0, width as i32, height as i32);
}
ctx.apply_bindings(&bindings);
pipeline.set_uniform("Projection", projection);
pipeline.set_uniform("Model", dc.model);
pipeline.set_uniform("_Time", time);
ctx.apply_uniforms_from_bytes(
pipeline.uniforms_data.as_ptr(),
pipeline.uniforms_data.len(),
);
ctx.draw(0, dc.indices_count as i32, 1);
dc.vertices_count = 0;
dc.indices_count = 0;
ctx.end_render_pass();
}
self.draw_calls_count = 0;
}
pub fn get_projection_matrix(&self) -> glam::Mat4 {
let ctx = crate::get_context();
ctx.draw_context.projection_matrix(&mut ctx.quad_context)
}
pub fn get_active_render_pass(&self) -> Option<RenderPass> {
self.state.render_pass
}
pub fn render_pass(&mut self, render_pass: Option<RenderPass>) {
self.state.render_pass = render_pass;
}
pub fn depth_test(&mut self, enable: bool) {
self.state.depth_test_enable = enable;
}
pub fn texture(&mut self, texture: Option<Texture2D>) {
self.state.texture = texture.map_or(self.white_texture, |t| t.texture);
}
pub fn scissor(&mut self, clip: Option<(i32, i32, i32, i32)>) {
self.state.clip = clip;
}
pub fn push_model_matrix(&mut self, matrix: glam::Mat4) {
self.state.model_stack.push(self.state.model() * matrix);
}
pub fn pop_model_matrix(&mut self) {
if self.state.model_stack.len() > 1 {
self.state.model_stack.pop();
}
}
pub fn pipeline(&mut self, pipeline: Option<GlPipeline>) {
self.state.pipeline = pipeline;
}
pub fn draw_mode(&mut self, mode: DrawMode) {
self.state.draw_mode = mode;
}
pub fn geometry(&mut self, vertices: &[impl Into<VertexInterop> + Copy], indices: &[u16]) {
assert!(vertices.len() <= MAX_VERTICES);
assert!(indices.len() <= MAX_INDICES);
let pip = self.state.pipeline.unwrap_or(
self.pipelines
.get(self.state.draw_mode, self.state.depth_test_enable),
);
let previous_dc_ix = if self.draw_calls_count == 0 {
None
} else {
Some(self.draw_calls_count - 1)
};
let previous_dc = previous_dc_ix.and_then(|ix| self.draw_calls.get(ix));
if previous_dc.map_or(true, |draw_call| {
draw_call.texture != self.state.texture
|| draw_call.clip != self.state.clip
|| draw_call.model != self.state.model()
|| draw_call.pipeline != pip
|| draw_call.render_pass != self.state.render_pass
|| draw_call.draw_mode != self.state.draw_mode
|| draw_call.vertices_count >= MAX_VERTICES - vertices.len()
|| draw_call.indices_count >= MAX_INDICES - indices.len()
}) {
if self.draw_calls_count >= self.draw_calls.len() {
self.draw_calls.push(DrawCall::new(
self.state.texture,
self.state.model(),
self.state.draw_mode,
pip,
self.state.render_pass,
));
}
self.draw_calls[self.draw_calls_count].texture = self.state.texture;
self.draw_calls[self.draw_calls_count].vertices_count = 0;
self.draw_calls[self.draw_calls_count].indices_count = 0;
self.draw_calls[self.draw_calls_count].clip = self.state.clip;
self.draw_calls[self.draw_calls_count].model = self.state.model();
self.draw_calls[self.draw_calls_count].pipeline = pip;
self.draw_calls[self.draw_calls_count].render_pass = self.state.render_pass;
self.draw_calls_count += 1;
};
let dc = &mut self.draw_calls[self.draw_calls_count - 1];
for i in 0..vertices.len() {
dc.vertices[dc.vertices_count + i] = vertices[i].into().into();
}
for i in 0..indices.len() {
dc.indices[dc.indices_count + i] = indices[i] + dc.vertices_count as u16;
}
dc.vertices_count += vertices.len();
dc.indices_count += indices.len();
dc.texture = self.state.texture;
}
pub fn delete_pipeline(&mut self, pipeline: GlPipeline) {
self.pipelines.delete_pipeline(pipeline);
}
pub fn set_uniform<T>(&mut self, pipeline: GlPipeline, name: &str, uniform: T) {
self.pipelines
.get_quad_pipeline_mut(pipeline)
.set_uniform(name, uniform);
}
pub fn set_texture(&mut self, pipeline: GlPipeline, name: &str, texture: Texture2D) {
let pipeline = self.pipelines.get_quad_pipeline_mut(pipeline);
pipeline
.textures
.iter()
.find(|x| *x == name)
.unwrap_or_else(|| {
panic!(
"can't find texture with name '{}', there is only this names: {:?}",
name, pipeline.textures
)
});
*pipeline
.textures_data
.entry(name.to_owned())
.or_insert(texture.texture) = texture.texture;
}
}
mod shader {
use miniquad::{ShaderMeta, UniformBlockLayout, UniformDesc, UniformType};
pub const VERTEX: &str = r#"#version 100
attribute vec3 position;
attribute vec2 texcoord;
attribute vec4 color0;
varying lowp vec2 uv;
varying lowp vec4 color;
uniform mat4 Model;
uniform mat4 Projection;
void main() {
gl_Position = Projection * Model * vec4(position, 1);
color = color0 / 255.0;
uv = texcoord;
}"#;
pub const FRAGMENT: &str = r#"#version 100
varying lowp vec4 color;
varying lowp vec2 uv;
uniform sampler2D Texture;
void main() {
gl_FragColor = color * texture2D(Texture, uv) ;
}"#;
pub fn uniforms() -> Vec<(&'static str, UniformType)> {
vec![
("Projection", UniformType::Mat4),
("Model", UniformType::Mat4),
("_Time", UniformType::Float4),
]
}
pub fn meta() -> ShaderMeta {
ShaderMeta {
images: vec!["Texture".to_string(), "_ScreenTexture".to_string()],
uniforms: UniformBlockLayout {
uniforms: uniforms()
.into_iter()
.map(|(name, kind)| UniformDesc::new(name, kind))
.collect(),
},
}
}
}