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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::{
    asset_protocols::font::FontAsset,
    components::{
        material_instance::HaMaterialInstance, mesh_instance::HaMeshInstance,
        text_instance::HaTextInstance,
    },
    constants::material_uniforms::*,
    ha_renderer::HaRenderer,
    image::{ImageReference, ImageResourceMapping},
    material::{
        common::MaterialValue,
        domains::surface::{text::SurfaceTextFactory, SurfaceVertexText},
    },
    mesh::{Mesh, MeshId, MeshReference},
};
use core::{
    assets::{asset::AssetId, database::AssetsDatabase},
    ecs::{life_cycle::EntityChanges, Comp, Entity, Universe, WorldRef},
};
use std::collections::HashMap;

#[derive(Debug, Default)]
pub struct HaFontSystemCache {
    fonts_map: HashMap<String, AssetId>,
    fonts_table: HashMap<AssetId, String>,
    meshes: HashMap<Entity, MeshId>,
}

pub type HaFontSystemResources<'a> = (
    WorldRef,
    &'a mut HaRenderer,
    &'a AssetsDatabase,
    &'a EntityChanges,
    &'a ImageResourceMapping,
    &'a mut HaFontSystemCache,
    Comp<&'a mut HaTextInstance>,
    Comp<&'a mut HaMeshInstance>,
    Comp<&'a mut HaMaterialInstance>,
);

pub fn ha_font_system(universe: &mut Universe) {
    let (world, mut renderer, assets, changes, image_mapping, mut cache, ..) =
        universe.query_resources::<HaFontSystemResources>();

    for id in assets.lately_loaded_protocol("font") {
        if let Some(asset) = assets.asset_by_id(*id) {
            if asset.is::<FontAsset>() {
                cache.fonts_map.insert(asset.path().to_owned(), *id);
                cache.fonts_table.insert(*id, asset.path().to_owned());
            }
        }
    }
    for id in assets.lately_unloaded_protocol("font") {
        if let Some(name) = cache.fonts_table.remove(id) {
            cache.fonts_map.remove(&name);
        }
    }

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

    for (entity, (text, mesh, material)) in world
        .query::<(
            &mut HaTextInstance,
            &mut HaMeshInstance,
            &mut HaMaterialInstance,
        )>()
        .iter()
    {
        if text.dirty || !cache.meshes.contains_key(&entity) {
            mesh.reference = MeshReference::None;
            if let Some(id) = cache.fonts_map.get(text.font()) {
                if let Some(asset) = assets.asset_by_id(*id) {
                    if let Some(asset) = asset.get::<FontAsset>() {
                        if let Ok(factory) =
                            SurfaceTextFactory::factory::<SurfaceVertexText>(text, asset)
                        {
                            if let Some(id) = cache.meshes.get(&entity) {
                                if let Some(m) = renderer.mesh_mut(*id) {
                                    m.set_vertex_storage_all(text.change_frequency().into());
                                    m.set_index_storage(text.change_frequency().into());
                                    if factory.write_into(m).is_ok() {
                                        text.dirty = false;
                                        mesh.reference = MeshReference::Id(*id);
                                        set_material_sampler(material, asset, &image_mapping);
                                    }
                                }
                            } else {
                                let mut m = Mesh::new(factory.layout().to_owned());
                                m.set_vertex_storage_all(text.change_frequency().into());
                                m.set_index_storage(text.change_frequency().into());
                                if factory.write_into(&mut m).is_ok() {
                                    if let Ok(id) = renderer.add_mesh(m) {
                                        text.dirty = false;
                                        mesh.reference = MeshReference::Id(id);
                                        set_material_sampler(material, asset, &image_mapping);
                                        cache.meshes.insert(entity, id);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

fn set_material_sampler(
    material: &mut HaMaterialInstance,
    font: &FontAsset,
    image_mapping: &ImageResourceMapping,
) {
    if let Some((_, id)) = font.pages_image_assets.get(0) {
        if let Some(id) = image_mapping.resource_by_asset(*id) {
            material.values.insert(
                MAIN_IMAGE_NAME.to_owned(),
                MaterialValue::Sampler2dArray {
                    reference: ImageReference::Id(id),
                    filtering: font.filtering,
                },
            );
        }
    }
}