twgpu 0.4.1

Render Teeworlds and DDNet maps
Documentation
use crate::{
    buffer::{GpuBuffer, GpuDeviceExt},
    Camera,
};
use twmap::{Group, Layer};
use vek::Vec2;
use wgpu::{BufferUsages, Device, Queue};

use super::{GpuEnvelopesData, GpuQuadsData, GpuTilemapData, TilemapCorner};

const LABEL: Option<&str> = Some("Group");

pub type GroupInfoBuffer = GpuBuffer<GroupInfo>;

#[derive(Debug, Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(C)]
/// Struct contained in the vertex buffer
pub struct GroupInfo {
    offset: Vec2<f32>,
    parallax: Vec2<f32>,
}

pub enum GpuLayerData {
    Tilemap(GpuTilemapData),
    Quads(GpuQuadsData),
}

pub struct GpuGroupData {
    pub info_buffer: GroupInfoBuffer,
    pub info: GroupInfo,
    pub layers: Vec<GpuLayerData>,
}

impl GpuGroupData {
    pub fn upload(
        group: &Group,
        envelopes: &GpuEnvelopesData,
        images: &[twmap::Image],
        device: &Device,
        queue: &Queue,
    ) -> Self {
        let info = GroupInfo {
            offset: group.offset.az(),
            parallax: group.parallax.az::<f32>() / 100.,
        };
        let info_buffer = device.buffer(&info, LABEL, BufferUsages::UNIFORM);
        let mut layers = Vec::new();
        for layer in &group.layers {
            if let Some(gpu_layer) = match layer {
                Layer::Tiles(layer) => Some(GpuLayerData::Tilemap(GpuTilemapData::upload(
                    layer, images, device, queue,
                ))),
                Layer::Quads(layer) => Some(GpuLayerData::Quads(GpuQuadsData::upload(
                    layer, envelopes, device,
                ))),
                _ => None,
            } {
                layers.push(gpu_layer);
            }
        }
        Self {
            info,
            info_buffer,
            layers,
        }
    }

    pub fn update(
        &self,
        vertices: &[Option<GpuBuffer<[TilemapCorner; 4]>>],
        camera: &Camera,
        render_target_size: Vec2<u32>,
        envelopes: &[twmap::Envelope],
        client_time: i64,
        server_time: i64,
        queue: &Queue,
    ) {
        let tiles_on_screen = camera.world_perspective_size(self.info.parallax);
        let mut projection = camera.world_perspective(self.info.parallax);
        projection.min += self.info.offset;
        projection.max += self.info.offset;

        for (layer, vertices) in self.layers.iter().zip(vertices.iter()) {
            if let GpuLayerData::Tilemap(tilemap) = layer {
                tilemap.update(
                    vertices.as_ref().unwrap(),
                    &projection,
                    tiles_on_screen,
                    render_target_size,
                    envelopes,
                    client_time,
                    server_time,
                    queue,
                );
            }
        }
    }
}