bevy_entitiles 0.3.0

A 2d tilemap library for bevy. With many useful algorithms/tools built in.
use bevy::{
    ecs::entity::Entity,
    prelude::{Commands, Query, Res, ResMut},
    render::renderer::{RenderDevice, RenderQueue},
};

use crate::tilemap::despawn::{DespawnedTile, DespawnedTilemap};

use super::{
    binding::TilemapBindGroups,
    buffer::{
        PerTilemapBuffersStorage, TilemapStorageBuffers, TilemapUniformBuffer, UniformBuffer,
    },
    chunk::{TilemapRenderChunk, UnloadRenderChunk},
    extract::{ExtractedTile, ExtractedTilemap},
    pipeline::EntiTilesPipeline,
    texture::TilemapTexturesStorage,
    RenderChunkStorage,
};

pub fn prepare_tilemaps(
    mut commands: Commands,
    render_device: Res<RenderDevice>,
    render_queue: Res<RenderQueue>,
    extracted_tilemaps: Query<&ExtractedTilemap>,
    mut render_chunks: ResMut<RenderChunkStorage>,
    mut uniform_buffers: ResMut<TilemapUniformBuffer>,
    mut storage_buffers: ResMut<TilemapStorageBuffers>,
    mut textures_storage: ResMut<TilemapTexturesStorage>,
    entitiles_pipeline: Res<EntiTilesPipeline>,
    mut bind_groups: ResMut<TilemapBindGroups>,
) {
    uniform_buffers.clear();
    storage_buffers.clear();

    extracted_tilemaps.for_each(|tilemap| {
        commands
            .entity(tilemap.id)
            .insert(uniform_buffers.insert(tilemap));

        render_chunks.prepare_chunks(tilemap, &render_device);

        if let Some(texture) = tilemap.texture.as_ref() {
            storage_buffers
                .get_or_insert_buffer(tilemap.id)
                .extend(&tilemap.animations.as_ref().unwrap().0);

            if !textures_storage.contains(&texture.texture) {
                textures_storage.insert(texture.clone_weak(), texture.desc());
            }
        }
    });

    #[cfg(not(feature = "atlas"))]
    textures_storage.prepare_textures(&render_device);
    uniform_buffers.write(&render_device, &render_queue);
    storage_buffers.write(&render_device, &render_queue);

    bind_groups.bind_uniform_buffers(&render_device, &mut uniform_buffers, &entitiles_pipeline);
    bind_groups.bind_storage_buffers(&render_device, &mut storage_buffers, &entitiles_pipeline);
}

pub fn prepare_tiles(
    extracted_tilemaps: Query<&ExtractedTilemap>,
    extracted_tiles: Query<&mut ExtractedTile>,
    mut render_chunks: ResMut<RenderChunkStorage>,
) {
    extracted_tiles.for_each(|tile| {
        let Ok(tilemap) = extracted_tilemaps.get(tile.tilemap_id) else {
            return;
        };

        let chunks = render_chunks.value.entry(tile.tilemap_id).or_default();

        let chunk = chunks
            .entry(tile.chunk_index)
            .or_insert_with(|| TilemapRenderChunk::from_index(tile.chunk_index, tilemap));

        chunk.set_tile(tile.in_chunk_index, Some(tile));
    });
}

pub fn prepare_unloaded_chunks(
    mut render_chunks: ResMut<RenderChunkStorage>,
    extracted_tilemaps: Query<(Entity, &UnloadRenderChunk)>,
) {
    extracted_tilemaps.for_each(|(entity, unloaded)| {
        unloaded.0.iter().for_each(|c| {
            render_chunks.remove_chunk(entity, *c);
        });
    });
}

pub fn prepare_despawned_tilemaps(
    mut render_chunks: ResMut<RenderChunkStorage>,
    mut storage_buffers: ResMut<TilemapStorageBuffers>,
    tilemaps_query: Query<&DespawnedTilemap>,
) {
    tilemaps_query.for_each(|map| {
        render_chunks.remove_tilemap(map.0);
        storage_buffers.remove(map.0);
    });
}

pub fn prepare_despawned_tiles(
    mut render_chunks: ResMut<RenderChunkStorage>,
    tiles_query: Query<&DespawnedTile>,
) {
    tiles_query.for_each(|tile| {
        if let Some(chunk) = render_chunks
            .get_chunks_mut(tile.tilemap)
            .and_then(|chunks| chunks.get_mut(&tile.chunk_index))
        {
            chunk.set_tile(tile.in_chunk_index, None);
        }
    });
}