sge_rendering 1.1.0

Rendering functionality for SGE
Documentation
use glium::vertex::Vertex as GliumVertex;
use glium::{Blend, DrawParameters, IndexBuffer, Surface, VertexBuffer, uniform};
use glium::{Depth, DepthTest, implement_vertex};
use sge_color::Color;
use sge_config::{get_dithering, get_polygon_mode};
use sge_debugging::*;
use sge_math::transform::Transform2D;
use sge_shapes::d2::{QUAD_INDICES, Shape2D, UNIT_QUAD};
use sge_textures::TextureRef;
use sge_types::{
    ColorVertex2D, LineBatch, MeshBatch, MetaballBatch, Pattern, PointBatch, Sdf, SdfBatch,
};
use sge_vectors::{Mat4, Rect, Vec2, Vec3};
use sge_window::get_display;

use crate::scissor::current_scissor;

pub use queue::*;
pub use scene::*;

mod queue;
mod scene;

#[derive(Clone)]
pub enum DrawCommand {
    Sdf(SdfBatch),
    Mesh(MeshBatch),
    Points(PointBatch),
    Lines(LineBatch),
    Sprites(SpriteBatch),
    Metaballs(*const MetaballBatch),
}

#[derive(Clone)]
pub struct SpriteBatch {
    texture: TextureRef,
    vertices: Vec<SpriteVertex>,
    indices: Vec<u32>,
    scissor: Option<glium::Rect>,
}

implement_vertex!(SpriteVertex, position, tex_coords, color);
#[derive(Copy, Clone, Debug)]
pub struct SpriteVertex {
    pub position: [f32; 3],
    pub tex_coords: [f32; 2],
    pub color: [f32; 4],
}

#[derive(Clone, Copy)]
pub enum RendererType {
    Screen,
    World,
    Scene,
    Other,
}

#[derive(Clone, Copy)]
pub struct Renderer2D {
    draws: *mut Vec<DrawCommand>,
    ty: RendererType,
}

impl Renderer2D {
    fn draws_mut(&mut self) -> &mut Vec<DrawCommand> {
        unsafe { &mut *self.draws }
    }

    fn draws(&self) -> &[DrawCommand] {
        unsafe { &*self.draws }
    }

    pub fn is_world(&self) -> bool {
        matches!(self.ty, RendererType::World)
    }

    pub fn current_sprite_batch(&mut self, texture: TextureRef) -> &mut SpriteBatch {
        let scissor = current_scissor();

        let can_merge = match self.draws().last() {
            Some(DrawCommand::Sprites(b)) => b.texture == texture && b.scissor == scissor,
            _ => false,
        };

        if !can_merge {
            self.draws_mut().push(DrawCommand::Sprites(SpriteBatch {
                texture,
                vertices: Vec::new(),
                indices: Vec::new(),
                scissor,
            }));
        }

        match self.draws_mut().last_mut().unwrap() {
            DrawCommand::Sprites(b) => b,
            _ => unreachable!(),
        }
    }

    pub fn current_sdf_batch(&mut self) -> &mut SdfBatch {
        let scissor = current_scissor();
        let needs_new = match self.draws().last() {
            Some(DrawCommand::Sdf(b)) => b.scissor != scissor,
            _ => true,
        };
        if needs_new {
            self.draws_mut()
                .push(DrawCommand::Sdf(SdfBatch::new(scissor)));
        }
        match self.draws_mut().last_mut().unwrap() {
            DrawCommand::Sdf(b) => b,
            _ => unreachable!(),
        }
    }

    pub fn add_sdf(&mut self, instance: Sdf) {
        debugger_add_drawn_objects(1);
        self.current_sdf_batch().instances.push(instance);
    }

    pub fn add_shape(&mut self, shape: &impl Shape2D) {
        self.add_sdf(shape.sdf());
    }

    pub fn current_draw_n(&self) -> usize {
        self.draws().len()
    }

    pub fn add_metaball_batch(&mut self, batch: &MetaballBatch) {
        debugger_add_drawn_objects(batch.len());
        self.draws_mut()
            .push(DrawCommand::Metaballs(batch as *const MetaballBatch));
    }

    pub fn add_sprite(
        &mut self,
        texture: TextureRef,
        mut transform: Transform2D,
        color: Color,
        region: Option<Rect>,
    ) {
        debugger_add_drawn_objects(1);

        let (tex_min_x, tex_min_y, tex_max_x, tex_max_y) = if let Some(region) = region {
            let tex = texture.get();
            let tex_width = tex.gl_texture.width() as f32;
            let tex_height = tex.gl_texture.height() as f32;
            (
                region.min.x / tex_width,
                region.min.y / tex_height,
                region.max.x / tex_width,
                region.max.y / tex_height,
            )
        } else {
            (0.0, 0.0, 1.0, 1.0)
        };

        let color_gpu = color.for_gpu();
        let mat = transform.matrix();

        let corners = [
            Vec3::new(0.0, 0.0, 0.0),
            Vec3::new(1.0, 0.0, 0.0),
            Vec3::new(1.0, 1.0, 0.0),
            Vec3::new(0.0, 1.0, 0.0),
        ];

        let tex_coords = [
            [tex_min_x, tex_min_y],
            [tex_max_x, tex_min_y],
            [tex_max_x, tex_max_y],
            [tex_min_x, tex_max_y],
        ];

        let batch = self.current_sprite_batch(texture);
        let base_index = batch.vertices.len() as u32;

        for i in 0..4 {
            let v = mat.transform_point3(corners[i]);
            batch.vertices.push(SpriteVertex {
                #[cfg(not(feature = "round_coords"))]
                position: [v.x, v.y, 0.0],
                #[cfg(feature = "round_coords")]
                position: [v.x.round(), v.y.round(), 0.0],
                tex_coords: tex_coords[i],
                color: color_gpu,
            });
        }

        batch.indices.extend_from_slice(&[
            base_index,
            base_index + 1,
            base_index + 2,
            base_index,
            base_index + 2,
            base_index + 3,
        ]);
    }

    pub fn current_mesh_batch(&mut self) -> &mut MeshBatch {
        let scissor = current_scissor();
        let needs_new = match self.draws().last() {
            Some(DrawCommand::Mesh(b)) => b.scissor != scissor,
            _ => true,
        };
        if needs_new {
            self.draws_mut()
                .push(DrawCommand::Mesh(MeshBatch::new(scissor)));
        }
        match self.draws_mut().last_mut().unwrap() {
            DrawCommand::Mesh(b) => b,
            _ => unreachable!(),
        }
    }

    pub fn add_mesh(&mut self, vertices: &[ColorVertex2D], indices: &[u32]) {
        let batch = self.current_mesh_batch();
        let base_index = batch.max_index;
        for v in vertices {
            batch.vertices.push(v.solid_pattern());
        }
        batch.indices.extend(indices.iter().map(|i| i + base_index));
        batch.max_index += vertices.len() as u32;
    }

    pub fn add_mesh_with_pattern(
        &mut self,
        vertices: &[ColorVertex2D],
        indices: &[u32],
        alt_color: Color,
        pattern: Pattern,
        scale: f32,
    ) {
        let batch = self.current_mesh_batch();
        let base_index = batch.max_index;
        for v in vertices {
            batch.vertices.push(v.to_pattern(alt_color, pattern, scale));
        }
        batch.indices.extend(indices.iter().map(|i| i + base_index));
        batch.max_index += vertices.len() as u32;
    }

    pub fn add_scene(&mut self, scene: &Scene2D) {
        self.draws_mut().append(&mut scene.clone().draws)
    }
    pub fn current_point_batch(&mut self) -> &mut PointBatch {
        let scissor = current_scissor();
        let needs_new = match self.draws().last() {
            Some(DrawCommand::Points(b)) => b.scissor != scissor,
            _ => true,
        };
        if needs_new {
            self.draws_mut()
                .push(DrawCommand::Points(PointBatch::new(scissor)));
        }
        match self.draws_mut().last_mut().unwrap() {
            DrawCommand::Points(b) => b,
            _ => unreachable!(),
        }
    }

    pub fn current_line_batch(&mut self) -> &mut LineBatch {
        let scissor = current_scissor();
        let needs_new = match self.draws().last() {
            Some(DrawCommand::Lines(b)) => b.scissor != scissor,
            _ => true,
        };
        if needs_new {
            self.draws_mut()
                .push(DrawCommand::Lines(LineBatch::new(scissor)));
        }
        match self.draws_mut().last_mut().unwrap() {
            DrawCommand::Lines(b) => b,
            _ => unreachable!(),
        }
    }

    pub fn add_pixel(&mut self, pos: Vec2, color: Color) {
        debugger_add_drawn_objects(1);
        let batch = self.current_point_batch();
        batch
            .vertices
            .push(ColorVertex2D::new(pos.x, pos.y, color).solid_pattern());
        batch.max_index += 1;
    }

    pub fn add_pixel_line(&mut self, a: Vec2, b: Vec2, color: Color) {
        debugger_add_drawn_objects(1);
        let batch = self.current_line_batch();
        let base_index = batch.max_index;
        batch
            .vertices
            .push(ColorVertex2D::new(a.x, a.y, color).solid_pattern());
        batch
            .vertices
            .push(ColorVertex2D::new(b.x, b.y, color).solid_pattern());
        batch
            .indices
            .extend_from_slice(&[base_index, base_index + 1]);
        batch.max_index += 2;
    }
}