1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use crate::{
    components::{
        material_instance::HaMaterialInstance, mesh_instance::HaMeshInstance,
        tilemap_instance::HaTileMapInstance,
    },
    ha_renderer::HaRenderer,
    image::{ImageFiltering, ImageId, ImageReference, VirtualImageSource},
    material::{
        common::MaterialValue,
        domains::surface::{tilemap::SurfaceTileMapFactory, SurfaceVertexPT},
    },
    mesh::{Mesh, MeshId, MeshReference},
};
use core::ecs::{life_cycle::EntityChanges, Comp, Entity, Universe, WorldRef};
use std::collections::HashMap;

#[derive(Debug, Default)]
pub struct HaTileMapSystemCache {
    meshes: HashMap<Entity, MeshId>,
}

pub type HaTileMapSystemResources<'a> = (
    WorldRef,
    &'a mut HaRenderer,
    &'a EntityChanges,
    &'a mut HaTileMapSystemCache,
    Comp<&'a mut HaTileMapInstance>,
    Comp<&'a mut HaMeshInstance>,
    Comp<&'a mut HaMaterialInstance>,
);

pub fn ha_tilemap_system(universe: &mut Universe) {
    let (world, mut renderer, changes, mut cache, ..) =
        universe.query_resources::<HaTileMapSystemResources>();

    for entity in changes.despawned() {
        if let Some(id) = cache.meshes.remove(&entity) {
            let _ = renderer.remove_mesh(id);
        }
    }

    for (entity, (tilemap, mesh, material)) in world
        .query::<(
            &mut HaTileMapInstance,
            &mut HaMeshInstance,
            &mut HaMaterialInstance,
        )>()
        .iter()
    {
        if tilemap.dirty || !cache.meshes.contains_key(&entity) {
            mesh.reference = MeshReference::None;
            if let Ok(factory) =
                SurfaceTileMapFactory::factory::<SurfaceVertexPT>(tilemap, &renderer.virtual_images)
            {
                let image_id = match renderer
                    .virtual_images
                    .get_named(tilemap.atlas())
                    .unwrap()
                    .source()
                {
                    VirtualImageSource::Image(image_id) => Some(*image_id),
                    _ => None,
                };
                if let Some(image_id) = image_id {
                    if let Some(id) = cache.meshes.get(&entity) {
                        if let Some(m) = renderer.mesh_mut(*id) {
                            m.set_vertex_storage_all(tilemap.change_frequency().into());
                            m.set_index_storage(tilemap.change_frequency().into());
                            if factory.write_into(m).is_ok() {
                                tilemap.dirty = false;
                                mesh.reference = MeshReference::Id(*id);
                                set_material_sampler(material, image_id, tilemap.filtering);
                            }
                        }
                    } else {
                        let mut m = Mesh::new(factory.layout().to_owned());
                        m.set_vertex_storage_all(tilemap.change_frequency().into());
                        m.set_index_storage(tilemap.change_frequency().into());
                        if factory.write_into(&mut m).is_ok() {
                            if let Ok(id) = renderer.add_mesh(m) {
                                tilemap.dirty = false;
                                mesh.reference = MeshReference::Id(id);
                                set_material_sampler(material, image_id, tilemap.filtering);
                                cache.meshes.insert(entity, id);
                            }
                        }
                    }
                }
            }
        }
    }
}

fn set_material_sampler(material: &mut HaMaterialInstance, id: ImageId, filtering: ImageFiltering) {
    material.values.insert(
        "mainImage".to_owned(),
        MaterialValue::Sampler2d {
            reference: ImageReference::Id(id),
            filtering,
        },
    );
}