shadow_engine_2d 2.0.1

A modern, high-performance 2D game engine built in Rust with ECS, physics, particles, audio, and more
Documentation
use image::GenericImageView;
use std::collections::HashMap;
use std::path::Path;

pub struct TextureManager {
    #[allow(dead_code)]
    textures: HashMap<String, Texture>,
    #[allow(dead_code)]
    device: wgpu::Device,
    #[allow(dead_code)]
    queue: wgpu::Queue,
}

pub struct Texture {
    pub texture: wgpu::Texture,
    pub view: wgpu::TextureView,
    pub sampler: wgpu::Sampler,
    pub size: (u32, u32),
}

impl Texture {
    pub fn from_bytes(
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        bytes: &[u8],
        label: &str,
    ) -> Result<Self, image::ImageError> {
        let img = image::load_from_memory(bytes)?;
        Self::from_image(device, queue, &img, Some(label))
    }

    pub fn from_image(
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        img: &image::DynamicImage,
        label: Option<&str>,
    ) -> Result<Self, image::ImageError> {
        let rgba = img.to_rgba8();
        let dimensions = img.dimensions();

        let size = wgpu::Extent3d {
            width: dimensions.0,
            height: dimensions.1,
            depth_or_array_layers: 1,
        };

        let texture = device.create_texture(&wgpu::TextureDescriptor {
            label,
            size,
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format: wgpu::TextureFormat::Rgba8UnormSrgb,
            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
            view_formats: &[],
        });

        queue.write_texture(
            wgpu::ImageCopyTexture {
                texture: &texture,
                mip_level: 0,
                origin: wgpu::Origin3d::ZERO,
                aspect: wgpu::TextureAspect::All,
            },
            &rgba,
            wgpu::ImageDataLayout {
                offset: 0,
                bytes_per_row: Some(4 * dimensions.0),
                rows_per_image: Some(dimensions.1),
            },
            size,
        );

        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
            address_mode_u: wgpu::AddressMode::ClampToEdge,
            address_mode_v: wgpu::AddressMode::ClampToEdge,
            address_mode_w: wgpu::AddressMode::ClampToEdge,
            mag_filter: wgpu::FilterMode::Nearest,
            min_filter: wgpu::FilterMode::Nearest,
            mipmap_filter: wgpu::FilterMode::Nearest,
            ..Default::default()
        });

        Ok(Texture {
            texture,
            view,
            sampler,
            size: dimensions,
        })
    }

    pub fn load<P: AsRef<Path>>(
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        path: P,
    ) -> Result<Self, image::ImageError> {
        let img = image::open(path)?;
        Self::from_image(device, queue, &img, None)
    }
}

#[derive(Clone, Debug)]
pub struct SpriteSheet {
    pub texture_name: String,
    pub tile_width: u32,
    pub tile_height: u32,
    pub columns: u32,
    pub rows: u32,
}

impl SpriteSheet {
    pub fn new(texture_name: impl Into<String>, tile_width: u32, tile_height: u32) -> Self {
        Self {
            texture_name: texture_name.into(),
            tile_width,
            tile_height,
            columns: 0,
            rows: 0,
        }
    }

    pub fn get_uv_coords(&self, index: u32) -> (f32, f32, f32, f32) {
        let col = index % self.columns;
        let row = index / self.columns;

        let u1 = col as f32 / self.columns as f32;
        let v1 = row as f32 / self.rows as f32;
        let u2 = (col + 1) as f32 / self.columns as f32;
        let v2 = (row + 1) as f32 / self.rows as f32;

        (u1, v1, u2, v2)
    }
}