bevy_entitiles 0.1.1

A tilemap library for bevy. With many algorithms built in.
use bevy::prelude::{Component, Query, ResMut};

use crate::{
    math::{
        aabb::AabbBox2d,
        extension::Vec2ToUVec2,
        tilemap::world_pos_to_chunk_square,
    },
    tilemap::TileType,
};

use super::{
    chunk::RenderChunkStorage,
    extract::{ExtractedTilemap, ExtractedView},
};

#[derive(Component)]
pub struct Visible;

pub fn cull(
    visible_tilemaps: Query<&ExtractedTilemap>,
    mut render_chunk_storage: ResMut<RenderChunkStorage>,
    cameras: Query<&ExtractedView>,
) {
    for tilemap in visible_tilemaps.iter() {
        for camera in cameras.iter() {
            let Some(chunks) = render_chunk_storage.get_chunks_mut(tilemap.id) else {
                break;
            };

            for chunk in chunks.iter_mut() {
                if let Some(c) = chunk {
                    c.visible = false;
                }
            }

            let camera_aabb = AabbBox2d::from_camera(camera);

            match tilemap.tile_type {
                TileType::Square => cull_square(&camera_aabb, tilemap, &mut render_chunk_storage),
                TileType::IsometricDiamond => {
                    cull_isometric_diamond(&camera_aabb, tilemap, &mut render_chunk_storage)
                }
            }
        }
    }
}

fn cull_square(
    camera_aabb: &AabbBox2d,
    tilemap: &ExtractedTilemap,
    render_chunk_storage: &mut ResMut<RenderChunkStorage>,
) {
    let Some(storage_size) = render_chunk_storage.get_storage_size(tilemap.id) else {
        return;
    };
    let chunks = render_chunk_storage.get_chunks_mut(tilemap.id).unwrap();

    let min_chunk_index = world_pos_to_chunk_square(
        tilemap.translation,
        tilemap.render_chunk_size,
        tilemap.tile_render_size,
        camera_aabb.min,
    )
    .floor_to_uvec();
    let max_chunk_index = world_pos_to_chunk_square(
        tilemap.translation,
        tilemap.render_chunk_size,
        tilemap.tile_render_size,
        camera_aabb.max,
    )
    .ceil_to_uvec();

    for index_y in min_chunk_index.y..max_chunk_index.y {
        for index_x in min_chunk_index.x..max_chunk_index.x {
            if let Some(Some(chunk)) = chunks.get_mut((index_y * storage_size.x + index_x) as usize)
            {
                chunk.visible = true;
            }
        }
    }
}

fn cull_isometric_diamond(
    camera_aabb: &AabbBox2d,
    tilemap: &ExtractedTilemap,
    render_chunk_storage: &mut ResMut<RenderChunkStorage>,
) {
    let chunks = render_chunk_storage.get_chunks_mut(tilemap.id).unwrap();

    for chunk in chunks.iter_mut() {
        if let Some(c) = chunk {
            if c.aabb.is_intersected_with(camera_aabb) {
                c.visible = true;
            }
        }
    }
}