micro_games_kit/assets/
spine.rs1use super::texture::TextureAsset;
2use anput::world::World;
3use keket::{
4 database::{
5 handle::{AssetDependency, AssetHandle},
6 path::AssetPathStatic,
7 },
8 protocol::AssetProtocol,
9};
10use rusty_spine::{Atlas, SkeletonData, SkeletonJson};
11use std::{
12 collections::HashMap,
13 error::Error,
14 io::{Cursor, Read},
15 sync::Arc,
16};
17use zip::ZipArchive;
18
19#[derive(Debug)]
20pub struct SpineAsset {
21 pub atlas: Arc<Atlas>,
22 pub skeleton_data: Arc<SkeletonData>,
23 pub textures: HashMap<String, AssetPathStatic>,
24}
25
26pub struct SpineAssetProtocol;
27
28impl AssetProtocol for SpineAssetProtocol {
29 fn name(&self) -> &str {
30 "spine"
31 }
32
33 fn process_bytes(
34 &mut self,
35 handle: AssetHandle,
36 storage: &mut World,
37 bytes: Vec<u8>,
38 ) -> Result<(), Box<dyn Error>> {
39 let mut archive = ZipArchive::new(Cursor::new(bytes))?;
40 let mut atlas = None;
41 let mut skeleton_data = None;
42 let mut atlas_page_names = Vec::new();
43 for file_name in archive.file_names() {
44 if file_name.ends_with(".atlas") {
45 atlas = Some(file_name.to_string());
46 } else if file_name.ends_with(".json") {
47 skeleton_data = Some(file_name.to_string());
48 } else if file_name.ends_with(".png") {
49 atlas_page_names.push(file_name.to_string());
50 }
51 }
52 let Some(atlas_name) = atlas else {
53 return Err("No atlas file found in Spine package".into());
54 };
55 let Some(skeleton_data_name) = skeleton_data else {
56 return Err("No skeleton data file found in Spine package".into());
57 };
58 let path_part = storage
59 .component::<true, AssetPathStatic>(handle.entity())?
60 .path()
61 .to_owned();
62
63 let mut bytes = vec![];
64 archive.by_name(&atlas_name)?.read_to_end(&mut bytes)?;
65 let atlas = Arc::new(Atlas::new(&bytes, "")?);
66
67 bytes.clear();
68 archive
69 .by_name(&skeleton_data_name)?
70 .read_to_end(&mut bytes)?;
71 let skeleton_data = Arc::new(SkeletonJson::new(atlas.clone()).read_skeleton_data(&bytes)?);
72
73 bytes.clear();
74 let mut textures = HashMap::new();
75 for atlas_page_name in atlas_page_names {
76 let mut bytes = vec![];
77 archive.by_name(&atlas_page_name)?.read_to_end(&mut bytes)?;
78 let image = image::load_from_memory(&bytes)?.into_rgba8();
79 let path = AssetPathStatic::new(format!("texture://{path_part}/{atlas_page_name}"));
80 let asset = TextureAsset {
81 image,
82 cols: 1,
83 rows: 1,
84 };
85 let entity = storage.spawn((path.clone(), asset))?;
86 textures.insert(atlas_page_name, path);
87 storage.relate::<true, _>(AssetDependency, handle.entity(), entity)?;
88 }
89
90 storage.insert(
91 handle.entity(),
92 (SpineAsset {
93 atlas,
94 skeleton_data,
95 textures,
96 },),
97 )?;
98
99 Ok(())
100 }
101}