oxygengine_composite_renderer/
map_asset_protocol.rs1use 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}