1use bmf_parser::BMFont;
6use int_math::{URect, UVec2, Vec2};
7use limnus_app::prelude::{App, Plugin};
8use limnus_asset_registry::AssetRegistry;
9use limnus_assets::prelude::{Asset, AssetName, Id, RawWeakId, WeakId};
10use limnus_assets::Assets;
11use limnus_assets_loader::{
12 AssetLoader, ConversionError, ResourceStorage, WrappedAssetLoaderRegistry,
13};
14use limnus_local_resource::LocalResourceStorage;
15use tracing::debug;
16
17pub type FontRef = Id<Font>;
18pub type WeakFontRef = WeakId<Font>;
19
20#[derive(Debug, Asset)]
21pub struct Font {
22 font: BMFont,
23}
24
25pub struct FontPlugin;
26
27impl Plugin for FontPlugin {
28 fn build(&self, app: &mut App) {
29 {
30 let registry = app.resource_mut::<WrappedAssetLoaderRegistry>();
31 let loader = FontConverter::new();
32
33 registry.value.lock().unwrap().register_loader(loader);
34 }
35
36 app.insert_resource(Assets::<Font>::default());
37 }
38}
39
40#[derive(Default)]
41pub struct FontConverter;
42
43impl FontConverter {
44 #[must_use]
45 pub const fn new() -> Self {
46 Self {}
47 }
48}
49
50impl AssetLoader for FontConverter {
51 type AssetType = Font;
52
53 fn convert_and_insert(
54 &self,
55 id: RawWeakId,
56 octets: &[u8],
57 resources: &mut ResourceStorage,
58 _local_resources: &mut LocalResourceStorage,
59 ) -> Result<(), ConversionError> {
60 let name: AssetName;
61 {
62 let asset_container = resources.fetch::<AssetRegistry>();
63 name = asset_container
64 .name_raw(id)
65 .expect("should know about this Id");
66 }
67
68 debug!("convert from fnt {name}");
69 let font = BMFont::from_octets(octets)?;
70
71 debug!("font complete {name}");
72 let font_assets = resources.fetch_mut::<Assets<Font>>();
73
74 font_assets.set_raw(id, Font { font });
75
76 Ok(())
77 }
78}
79
80#[derive(Debug)]
81pub struct Glyph {
82 pub relative_position: Vec2,
83 pub texture_rectangle: URect,
84}
85
86impl Font {
87 #[must_use]
90 pub fn from_octets(bm_contents: &[u8]) -> Self {
91 let font = BMFont::from_octets(bm_contents).unwrap();
92 Self { font }
93 }
94
95 #[must_use]
98 pub fn draw(&self, text: &str) -> Vec<Glyph> {
99 let mut x = 0;
100 let y = 0;
101 let common = self.font.common.as_ref().unwrap();
102 let height = self.font.info.as_ref().unwrap().font_size;
103 let mut glyphs = Vec::new();
104 let factor = 1u16;
105 let y_offset = -(common.line_height as i16 - common.base as i16);
106 for ch in text.chars() {
107 if let Some(bm_char) = self.font.chars.get(&(ch as u32)) {
108 let cx = x + bm_char.x_offset * factor as i16;
109 let cy = y + y_offset + height - (bm_char.height as i16) - bm_char.y_offset;
110
111 let glyph = Glyph {
112 relative_position: Vec2 { x: cx, y: cy },
113 texture_rectangle: URect {
114 position: UVec2 {
115 x: bm_char.x,
116 y: bm_char.y,
117 },
118 size: UVec2 {
119 x: bm_char.width,
120 y: bm_char.height,
121 },
122 },
123 };
124 x += bm_char.x_advance * factor as i16;
125
126 glyphs.push(glyph);
127 }
128 }
129
130 glyphs
131 }
132}