aboutsummaryrefslogtreecommitdiff
path: root/light-client/src/atlas.rs
diff options
context:
space:
mode:
Diffstat (limited to 'light-client/src/atlas.rs')
-rw-r--r--light-client/src/atlas.rs154
1 files changed, 154 insertions, 0 deletions
diff --git a/light-client/src/atlas.rs b/light-client/src/atlas.rs
new file mode 100644
index 00000000..e75430c5
--- /dev/null
+++ b/light-client/src/atlas.rs
@@ -0,0 +1,154 @@
+use hurrycurry_protocol::{
+ glam::{IVec2, Vec2},
+ ClientGamedata, ItemIndex, TileIndex,
+};
+use sdl2::{
+ pixels::PixelFormatEnum,
+ rect::Rect,
+ render::{Canvas, Texture, TextureAccess, TextureCreator},
+ video::{Window, WindowContext},
+};
+use std::collections::HashMap;
+
+pub struct SpriteRenderer<'a> {
+ texture: Texture<'a>,
+
+ tiles: Vec<Rect>,
+ items: Vec<Rect>,
+
+ view_scale: u32,
+ view_offset: Vec2,
+
+ sprites: Vec<DrawItem>,
+}
+
+pub struct DrawItem {
+ z_order: i32,
+ src: Rect,
+ dst: Rect,
+}
+
+impl<'a> SpriteRenderer<'a> {
+ pub fn init(texture_creator: &'a TextureCreator<WindowContext>) -> Self {
+ let palette = include_str!("../textures/palette.csv")
+ .split('\n')
+ .filter(|l| !l.is_empty())
+ .map(|s| {
+ let mut toks = s.split(",");
+ (
+ toks.next().unwrap().chars().next().unwrap(),
+ [
+ toks.next().unwrap().parse::<u8>().unwrap(),
+ toks.next().unwrap().parse::<u8>().unwrap(),
+ toks.next().unwrap().parse::<u8>().unwrap(),
+ toks.next().unwrap().parse::<u8>().unwrap(),
+ ],
+ )
+ })
+ .collect::<HashMap<_, _>>();
+
+ let mut texels = vec![255; 1024 * 1024 * 4];
+
+ for (y, line) in include_str!("../textures/atlas.ta").lines().enumerate() {
+ if line.is_empty() {
+ continue;
+ }
+ for (x, char) in line.chars().enumerate() {
+ let color = palette.get(&char).unwrap();
+ let base = (y * 1024 + x) * 4;
+ texels[base..base + 4].copy_from_slice(color);
+ }
+ }
+
+ let mut texture = texture_creator
+ .create_texture(
+ Some(PixelFormatEnum::RGBA8888),
+ TextureAccess::Streaming,
+ 1024,
+ 1024,
+ )
+ .unwrap();
+
+ texture.update(None, &texels, 1024 * 4).unwrap();
+
+ Self {
+ texture,
+ items: vec![],
+ tiles: vec![],
+ sprites: vec![],
+ view_offset: Vec2::ZERO,
+ view_scale: 32,
+ }
+ }
+
+ pub fn set_sprite_map(&mut self, data: ClientGamedata) {
+ let meta = include_str!("../textures/atlas.meta.csv")
+ .lines()
+ .filter(|l| !l.is_empty())
+ .map(|l| {
+ let mut toks = l.split(",");
+ let x: i32 = toks.next().unwrap().parse().unwrap();
+ let y: i32 = toks.next().unwrap().parse().unwrap();
+ let w: u32 = toks.next().unwrap().parse().unwrap();
+ let h: u32 = toks.next().unwrap().parse().unwrap();
+ let name = toks.next().unwrap().to_string();
+ (name, Rect::new(x, y, w, h))
+ })
+ .collect::<HashMap<_, _>>();
+
+ self.items = data
+ .item_names
+ .iter()
+ .map(|i| meta.get(i).copied().unwrap_or(Rect::new(0, 0, 100, 100)))
+ .collect();
+ self.tiles = data
+ .tile_names
+ .iter()
+ .map(|i| meta.get(i).copied().unwrap_or(Rect::new(0, 0, 100, 100)))
+ .collect();
+ }
+
+ pub fn draw_tile(&mut self, TileIndex(i): TileIndex, position: IVec2) {
+ let p = (self.view_offset.as_ivec2() + position) * self.view_scale as i32;
+ self.sprites.push(DrawItem {
+ z_order: position.y,
+ src: self.tiles[i],
+ dst: Rect::from_center((p.x as i32, p.y as i32), self.view_scale, self.view_scale),
+ });
+ }
+ pub fn draw_item(&mut self, ItemIndex(i): ItemIndex, position: Vec2) {
+ self.sprites.push(DrawItem {
+ z_order: position.y as i32,
+ src: self.tiles[i],
+ dst: Rect::from_center(
+ (position.x as i32, position.y as i32),
+ self.view_scale,
+ self.view_scale,
+ ),
+ })
+ }
+
+ pub fn submit(&mut self, canvas: &mut Canvas<Window>) {
+ self.sprites.sort();
+ for DrawItem { src, dst, .. } in self.sprites.drain(..) {
+ canvas.copy(&self.texture, src, dst).unwrap();
+ }
+ }
+}
+
+impl Ord for DrawItem {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.z_order.cmp(&other.z_order)
+ }
+}
+impl PartialOrd for DrawItem {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(&other))
+ }
+}
+impl Eq for DrawItem {}
+impl PartialEq for DrawItem {
+ fn eq(&self, other: &Self) -> bool {
+ self.z_order == other.z_order && self.src == other.src && self.dst == other.dst
+ }
+}