pixel_engine_backend 0.8.0

An thin layer around wgpu that allow the modification of a texutre displayed on the screen
Documentation
use crate::Vertex;
use float_next_after::NextAfter;
use wgpu::util::DeviceExt;
pub type DecalTextureID = usize;

mod gpu_vector;

#[derive(Debug)]
pub struct DecalInstances {
    pub id: DecalTextureID,
    pub pos: [(f32, f32); 4],
    pub uv: [(f32, f32); 4],
    pub w: [f32; 4],
    pub tint: [f32; 4],
}

#[derive(Debug, Clone)]
pub struct Decal {
    id: DecalTextureID,
    pub size: (u32, u32),
    pub uv_scale: (f32, f32),
}

pub struct DecalContextManager {
    id_generator: DecalIDGenerator,
    decal_textures:
        std::collections::HashMap<DecalTextureID, (crate::texture::Texture, wgpu::BindGroup)>,
    pub decal_instances: Vec<DecalInstances>,
    vertex_vector: gpu_vector::GpuVector<[Vertex; 4]>,
    cpu_vertex_vector: Vec<[Vertex; 4]>,
    buffer_index: wgpu::Buffer,
}

impl DecalContextManager {
    #[must_use]
    pub fn new(
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        bind_group_layout: &wgpu::BindGroupLayout,
        spr: (&[u8], (u32, u32)),
    ) -> (Self, wgpu::CommandBuffer) {
        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
            label: Some("decal"),
        });
        let vertex_vector =
            gpu_vector::GpuVector::with_capacity(128, device, wgpu::BufferUsages::VERTEX);
        let buffer_index = {
            let b = device.create_buffer(&wgpu::BufferDescriptor {
                mapped_at_creation: false,
                label: None,
                size: std::mem::size_of::<[u16; 6]>() as u64,
                usage: wgpu::BufferUsages::COPY_DST
                    | wgpu::BufferUsages::INDEX
                    | wgpu::BufferUsages::COPY_SRC,
            });
            encoder.copy_buffer_to_buffer(
                &{
                    device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
                        label: None,
                        contents: bytemuck::cast_slice(crate::INDICES),
                        usage: wgpu::BufferUsages::INDEX
                            | wgpu::BufferUsages::COPY_DST
                            | wgpu::BufferUsages::COPY_SRC,
                    })
                },
                0,
                &b,
                0,
                std::mem::size_of::<[u16; 6]>() as u64,
            );
            b
        };
        (
            Self {
                id_generator: DecalIDGenerator(0),
                buffer_index,
                vertex_vector,
                decal_textures: {
                    let mut out = std::collections::HashMap::with_capacity(64);

                    let tex = crate::texture::Texture::from_bytes(device, queue, spr);
                    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
                        layout: bind_group_layout,
                        entries: &[
                            wgpu::BindGroupEntry {
                                binding: 0,
                                resource: wgpu::BindingResource::TextureView(&tex.view),
                            },
                            wgpu::BindGroupEntry {
                                binding: 1,
                                resource: wgpu::BindingResource::Sampler(&tex.sampler),
                            },
                        ],
                        label: Some("decal_bindgroup"),
                    });

                    out.insert(0usize, (tex, bind_group));
                    out
                },
                decal_instances: {
                    let mut out = Vec::with_capacity(128);
                    out.push(DecalInstances {
                        id: 0,
                        pos: {
                            use crate::CORNER;
                            [
                                (-CORNER, CORNER),
                                (-CORNER, -CORNER),
                                (CORNER, -CORNER),
                                (CORNER, CORNER),
                            ]
                        },
                        uv: [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)],
                        w: [1.0; 4],
                        tint: [1.0; 4],
                    });
                    out
                },
                cpu_vertex_vector: Vec::with_capacity(128),
            },
            encoder.finish(),
        )
    }
    pub fn add_instance(&mut self, decal: DecalInstances) {
        self.decal_instances.push(decal);
    }

    pub fn update_main_texture(&mut self, queue: &wgpu::Queue, data: &[u8]) {
        (self.decal_textures[&0].0).update(queue, data);
    }
}

#[derive(Debug, Clone)]
struct DecalIDGenerator(DecalTextureID);
impl DecalIDGenerator {
    fn get(&mut self) -> usize {
        self.0 += 1;
        self.0
    }
}

impl Decal {
    pub fn create(ctx: &mut crate::Context, sprite: (&[u8], (u32, u32))) -> Self {
        let id = ctx.dcm.id_generator.get();
        let tex = crate::texture::Texture::from_bytes(&ctx.device, &ctx.queue, sprite);
        let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
            layout: &ctx.bind_group_layout,
            entries: &[
                wgpu::BindGroupEntry {
                    binding: 0,
                    resource: wgpu::BindingResource::TextureView(&tex.view),
                },
                wgpu::BindGroupEntry {
                    binding: 1,
                    resource: wgpu::BindingResource::Sampler(&tex.sampler),
                },
            ],
            label: Some("decal_bindgroup"),
        });
        ctx.dcm.decal_textures.insert(id, (tex, bind_group));

        Self {
            id,
            size: sprite.1,
            uv_scale: (1.0 / (sprite.1).0 as f32, 1.0 / (sprite.1).1 as f32),
        }
    }

    pub fn destroy(&self, ctx: &mut crate::Context) {
        ctx.dcm.decal_textures.remove(&self.id);
    }

    #[must_use]
    pub fn id(&self) -> DecalTextureID {
        self.id
    }
}

pub trait DrawDecals<'a, 'b>
where
    'b: 'a,
{
    fn draw_decals(
        &mut self,
        dcm: &'b mut DecalContextManager,
        device: &'b mut wgpu::Device,
        queue: &'b mut wgpu::Queue,
    );
}

impl<'a, 'b> DrawDecals<'a, 'b> for wgpu::RenderPass<'a>
where
    'b: 'a,
{
    fn draw_decals(
        &mut self,
        dcm: &'b mut DecalContextManager,
        device: &'b mut wgpu::Device,
        queue: &'b mut wgpu::Queue,
    ) {
        let mut z = 0.0;
        for decal_instance in dcm.decal_instances.iter() {
            //z += 0.05;
            dcm.cpu_vertex_vector.push([
                Vertex {
                    position: [decal_instance.pos[0].0, decal_instance.pos[0].1, z],
                    tex_coords: [
                        decal_instance.uv[0].0,
                        decal_instance.uv[0].1,
                        decal_instance.w[0],
                    ],
                    tint: decal_instance.tint,
                },
                Vertex {
                    position: [decal_instance.pos[1].0, decal_instance.pos[1].1, z],
                    tex_coords: [
                        decal_instance.uv[1].0,
                        decal_instance.uv[1].1,
                        decal_instance.w[1],
                    ],
                    tint: decal_instance.tint,
                },
                Vertex {
                    position: [decal_instance.pos[2].0, decal_instance.pos[2].1, z],
                    tex_coords: [
                        decal_instance.uv[2].0,
                        decal_instance.uv[2].1,
                        decal_instance.w[2],
                    ],
                    tint: decal_instance.tint,
                },
                Vertex {
                    position: [decal_instance.pos[3].0, decal_instance.pos[3].1, z],
                    tex_coords: [
                        decal_instance.uv[3].0,
                        decal_instance.uv[3].1,
                        decal_instance.w[3],
                    ],
                    tint: decal_instance.tint,
                },
            ]);

            z = z.next_after(-f32::INFINITY);
        }

        let command = dcm.vertex_vector.sync(device, &dcm.cpu_vertex_vector);
        let buffer = dcm.vertex_vector.buffer();

        self.set_index_buffer(dcm.buffer_index.slice(..), wgpu::IndexFormat::Uint16);
        for (range, instance) in dcm
            .vertex_vector
            .iter_offsets()
            .zip(dcm.decal_instances.drain(..))
        {
            let Some(texture) = dcm.decal_textures.get(&instance.id) else {
                    eprintln!("You tried to use a non-valid decal");
                    continue
            };

            // Update buffers

            self.set_bind_group(0, &texture.1, &[]);
            self.set_vertex_buffer(0, buffer.slice(range));
            self.draw_indexed(0..(crate::INDICES.len() as u32), 0, 0..1);
        }
        dcm.cpu_vertex_vector.clear();
        dcm.decal_instances.push(DecalInstances {
            id: 0,
            pos: {
                use crate::CORNER;
                [
                    (-CORNER, CORNER),
                    (-CORNER, -CORNER),
                    (CORNER, -CORNER),
                    (CORNER, CORNER),
                ]
            },
            uv: [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)],
            w: [1.0; 4],
            tint: [1.0; 4],
        });
        queue.submit(std::iter::once(command));
    }
}