bevy_entitiles 0.11.0

A 2d tilemap library for bevy. With many useful algorithms/tools built in.
Documentation
use bevy::{
    asset::{AssetId, Handle},
    ecs::entity::EntityHashMap,
    prelude::{Res, ResMut, Resource},
    render::{
        render_asset::RenderAssets,
        render_resource::{BindGroup, BindGroupEntries},
        renderer::RenderDevice,
        texture::{FallbackImage, GpuImage},
        view::ViewUniforms,
    },
    utils::HashMap,
};

use crate::{
    render::{
        buffer::TilemapBuffers,
        extract::{TilemapInstances, TilemapMaterialIds},
        material::{ExtractedTilemapMaterialWrapper, TilemapMaterial},
        pipeline::EntiTilesPipeline,
        texture::TilemapTexturesStorage,
    },
    tilemap::map::TilemapTextures,
};

#[derive(Resource, Default)]
pub struct TilemapBindGroups<M: TilemapMaterial> {
    pub uniform_buffer: Option<BindGroup>,
    pub array_buffers: EntityHashMap<BindGroup>,
    pub textures: HashMap<Handle<TilemapTextures>, BindGroup>,
    pub materials: HashMap<AssetId<M>, BindGroup>,
}

pub fn bind_tilemap_buffers<M: TilemapMaterial>(
    render_device: Res<RenderDevice>,
    entitiles_pipeline: Res<EntiTilesPipeline<M>>,
    tilemap_buffers: Res<TilemapBuffers>,
    mut bind_groups: ResMut<TilemapBindGroups<M>>,
    tilemap_instances: Res<TilemapInstances>,
    view_uniforms: Res<ViewUniforms>,
) {
    if let (Some(uniform_buffer), Some(view_binding)) = (
        tilemap_buffers.shared.uniform.binding(),
        view_uniforms.uniforms.binding(),
    ) {
        bind_groups.uniform_buffer = Some(render_device.create_bind_group(
            Some("tilemap_uniform_buffers_bind_group"),
            &entitiles_pipeline.uniform_buffers_layout,
            &BindGroupEntries::sequential((uniform_buffer, view_binding)),
        ));
    }

    for tilemap in tilemap_instances.keys() {
        let Some(buffers) = tilemap_buffers.unshared.get(tilemap) else {
            continue;
        };

        let Some(anim) = buffers.animation.binding() else {
            continue;
        };

        #[cfg(feature = "atlas")]
        let Some(desc) = buffers.texture_desc.binding() else {
            continue;
        };

        bind_groups.array_buffers.insert(
            *tilemap,
            render_device.create_bind_group(
                "tilemap_storage_buffers_bind_group",
                &entitiles_pipeline.array_buffers_layout,
                &BindGroupEntries::sequential((
                    anim,
                    #[cfg(feature = "atlas")]
                    desc,
                )),
            ),
        );
    }
}

pub fn bind_materials<M: TilemapMaterial>(
    pipeline: Res<EntiTilesPipeline<M>>,
    render_device: Res<RenderDevice>,
    images: Res<RenderAssets<GpuImage>>,
    fallback_image: Res<FallbackImage>,
    material_ids: Res<TilemapMaterialIds<M>>,
    mut bind_groups: ResMut<TilemapBindGroups<M>>,
    material_assets: Res<RenderAssets<ExtractedTilemapMaterialWrapper<M>>>,
) {
    for id in material_ids.values() {
        if let Some(material) = material_assets.get(*id) {
            let bind_group = material
                .as_bind_group(
                    &pipeline.material_layout,
                    &render_device,
                    &images,
                    &fallback_image,
                )
                .unwrap();
            bind_groups.materials.insert(*id, bind_group.bind_group);
        }
    }
}

pub fn bind_textures<M: TilemapMaterial>(
    tilemap_instances: Res<TilemapInstances>,
    render_device: Res<RenderDevice>,
    textures_storage: Res<TilemapTexturesStorage>,
    entitiles_pipeline: Res<EntiTilesPipeline<M>>,
    mut bind_groups: ResMut<TilemapBindGroups<M>>,
) {
    for tilemap in tilemap_instances.values() {
        let Some((handle, texture)) = tilemap
            .texture
            .as_ref()
            .and_then(|h| textures_storage.get_texture(h).map(|t| (h, t)))
        else {
            continue;
        };

        if !bind_groups.textures.contains_key(handle) {
            bind_groups.textures.insert(
                handle.clone(),
                render_device.create_bind_group(
                    Some("color_texture_bind_group"),
                    &entitiles_pipeline.texture_layout,
                    &BindGroupEntries::sequential((&texture.texture_view, &texture.sampler)),
                ),
            );
        }
    }
}