oxygengine_composite_renderer/
map_asset_protocol.rs

1use crate::{
2    composite_renderer::{Command, Image},
3    sprite_sheet_asset_protocol::SpriteSheetAsset,
4};
5use core::{
6    assets::{
7        asset::{Asset, AssetId},
8        database::AssetsDatabase,
9        protocol::{AssetLoadResult, AssetProtocol, AssetVariant, Meta},
10    },
11    Ignite, Scalar,
12};
13use serde::{Deserialize, Serialize};
14use std::{any::Any, collections::HashMap};
15
16#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
17pub struct LayerObject {
18    pub name: String,
19    pub object_type: String,
20    pub visible: bool,
21    pub x: isize,
22    pub y: isize,
23    pub width: usize,
24    pub height: usize,
25}
26
27#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
28pub enum LayerData {
29    Tiles(Vec<usize>),
30    Objects(Vec<LayerObject>),
31}
32
33impl LayerData {
34    pub fn tiles(&self) -> Option<&[usize]> {
35        if let LayerData::Tiles(data) = self {
36            Some(data)
37        } else {
38            None
39        }
40    }
41
42    pub fn objects(&self) -> Option<&[LayerObject]> {
43        if let LayerData::Objects(data) = self {
44            Some(data)
45        } else {
46            None
47        }
48    }
49}
50
51#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
52pub struct Layer {
53    pub name: String,
54    pub data: LayerData,
55}
56
57#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
58pub struct Map {
59    pub cols: usize,
60    pub rows: usize,
61    pub tile_width: usize,
62    pub tile_height: usize,
63    pub sprite_sheets: Vec<String>,
64    pub tiles_mapping: HashMap<usize, (String, String)>,
65    pub layers: Vec<Layer>,
66}
67
68impl Map {
69    pub fn size(&self) -> (usize, usize) {
70        (self.cols * self.tile_width, self.rows * self.tile_height)
71    }
72
73    pub fn layer_by_name(&self, name: &str) -> Option<&Layer> {
74        self.layers.iter().find(|layer| layer.name == name)
75    }
76
77    pub fn build_render_commands_from_layer_by_name<'a>(
78        &self,
79        name: &str,
80        chunk_offset: (usize, usize),
81        chunk_size: Option<(usize, usize)>,
82        assets: &AssetsDatabase,
83    ) -> Option<Vec<Command<'a>>> {
84        let index = self.layers.iter().position(|layer| layer.name == name)?;
85        self.build_render_commands_from_layer(index, chunk_offset, chunk_size, assets)
86    }
87
88    pub fn build_render_commands_from_layer<'a>(
89        &self,
90        index: usize,
91        chunk_offset: (usize, usize),
92        chunk_size: Option<(usize, usize)>,
93        assets: &AssetsDatabase,
94    ) -> Option<Vec<Command<'a>>> {
95        if self.tiles_mapping.is_empty() {
96            return None;
97        }
98        let layer = self.layers.get(index)?;
99        if let LayerData::Tiles(data) = &layer.data {
100            let atlases = self
101                .sprite_sheets
102                .iter()
103                .map(|s| {
104                    let info = assets
105                        .asset_by_path(&format!("atlas://{}", s))?
106                        .get::<SpriteSheetAsset>()?
107                        .info();
108                    Some((s.clone(), info))
109                })
110                .collect::<Option<HashMap<_, _>>>()?;
111            let mut commands = Vec::with_capacity(2 + self.cols * self.rows);
112            commands.push(Command::Store);
113            let width = self.tile_width as Scalar;
114            let height = self.tile_height as Scalar;
115            let chunk_size = chunk_size.unwrap_or((self.cols, self.rows));
116            let cols_start = chunk_offset.0.min(self.cols);
117            let cols_end = (chunk_offset.0 + chunk_size.0).min(self.cols);
118            let rows_start = chunk_offset.1.min(self.rows);
119            let rows_end = (chunk_offset.1 + chunk_size.1).min(self.rows);
120            for col in cols_start..cols_end {
121                for row in rows_start..rows_end {
122                    let i = self.cols * row + col;
123                    let id = data.get(i).unwrap_or(&0);
124                    if let Some((sprite_sheet, name)) = &self.tiles_mapping.get(id) {
125                        let info = atlases.get(sprite_sheet)?;
126                        let x = width * col as Scalar;
127                        let y = height * row as Scalar;
128                        let frame = info.frames.get(name)?.frame;
129                        commands.push(Command::Draw(
130                            Image::new_owned(info.meta.image_name())
131                                .source(Some(frame))
132                                .destination(Some([x, y, width, height].into()))
133                                .into(),
134                        ));
135                    }
136                }
137            }
138            commands.push(Command::Restore);
139            Some(commands)
140        } else {
141            None
142        }
143    }
144}
145
146pub struct MapAsset {
147    map: Map,
148    sprite_sheet_assets: Vec<AssetId>,
149}
150
151impl MapAsset {
152    pub fn map(&self) -> &Map {
153        &self.map
154    }
155
156    pub fn sprite_sheet_assets(&self) -> &[AssetId] {
157        &self.sprite_sheet_assets
158    }
159}
160
161pub struct MapAssetProtocol;
162
163impl AssetProtocol for MapAssetProtocol {
164    fn name(&self) -> &str {
165        "map"
166    }
167
168    fn on_load(&mut self, data: Vec<u8>) -> AssetLoadResult {
169        let map: Map = bincode::deserialize(&data).unwrap();
170        let list = map
171            .sprite_sheets
172            .iter()
173            .map(|s| (s.clone(), format!("atlas://{}", s)))
174            .collect::<Vec<_>>();
175        AssetLoadResult::Yield(Some(Box::new(map)), list)
176    }
177
178    fn on_resume(&mut self, payload: Meta, list: &[(&str, &Asset)]) -> AssetLoadResult {
179        let map = *(payload.unwrap() as Box<dyn Any + Send>)
180            .downcast::<Map>()
181            .unwrap();
182        let sprite_sheet_assets = list.iter().map(|(_, asset)| asset.id()).collect::<Vec<_>>();
183        AssetLoadResult::Data(Box::new(MapAsset {
184            map,
185            sprite_sheet_assets,
186        }))
187    }
188
189    fn on_unload(&mut self, asset: &Asset) -> Option<Vec<AssetVariant>> {
190        asset.get::<MapAsset>().map(|asset| {
191            asset
192                .sprite_sheet_assets
193                .iter()
194                .map(|a| AssetVariant::Id(*a))
195                .collect::<Vec<_>>()
196        })
197    }
198}