aboutsummaryrefslogtreecommitdiff
path: root/light-client/src/sprite_renderer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'light-client/src/sprite_renderer.rs')
-rw-r--r--light-client/src/sprite_renderer.rs152
1 files changed, 152 insertions, 0 deletions
diff --git a/light-client/src/sprite_renderer.rs b/light-client/src/sprite_renderer.rs
new file mode 100644
index 00000000..64eef8f6
--- /dev/null
+++ b/light-client/src/sprite_renderer.rs
@@ -0,0 +1,152 @@
+/*
+ Hurry Curry! - a game about cooking
+ Copyright 2024 metamuffin
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, version 3 of the License only.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+use hurrycurry_protocol::glam::Vec2;
+use sdl2::{
+ pixels::PixelFormatEnum,
+ rect::{FRect, Rect},
+ render::{BlendMode, Canvas, Texture, TextureAccess, TextureCreator},
+ video::{Window, WindowContext},
+};
+use std::collections::HashMap;
+
+pub struct SpriteRenderer<'a> {
+ metadata: HashMap<String, Rect>,
+ texture: Texture<'a>,
+
+ view_scale: Vec2,
+ view_offset: Vec2,
+
+ sprites: Vec<SpriteDraw>,
+}
+
+pub struct SpriteDraw {
+ z_order: i32,
+ src: Rect,
+ dst: FRect,
+}
+
+impl<'a> SpriteRenderer<'a> {
+ pub fn init(texture_creator: &'a TextureCreator<WindowContext>) -> Self {
+ let palette = include_str!("../assets/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!("../assets/atlas.ta").lines().enumerate() {
+ if line.is_empty() {
+ continue;
+ }
+ for (x, char) in line.chars().enumerate() {
+ let color = palette.get(&char).unwrap();
+ texels[(y * 1024 + x) * 4 + 0] = color[3];
+ texels[(y * 1024 + x) * 4 + 1] = color[2];
+ texels[(y * 1024 + x) * 4 + 2] = color[1];
+ texels[(y * 1024 + x) * 4 + 3] = color[0];
+ }
+ }
+
+ let mut texture = texture_creator
+ .create_texture(
+ Some(PixelFormatEnum::RGBA8888),
+ TextureAccess::Streaming,
+ 1024,
+ 1024,
+ )
+ .unwrap();
+
+ texture.update(None, &texels, 1024 * 4).unwrap();
+ texture.set_blend_mode(BlendMode::Blend);
+
+ let metadata = include_str!("../assets/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 {
+ texture,
+ metadata,
+ sprites: vec![],
+ view_offset: Vec2::ZERO,
+ view_scale: Vec2::splat(64.),
+ }
+ }
+
+ pub fn metadata(&self) -> &HashMap<String, Rect> {
+ &self.metadata
+ }
+
+ pub fn draw(&mut self, z_order: i32, src: Rect, dst: FRect) {
+ self.sprites.push(SpriteDraw {
+ z_order,
+ src,
+ dst: FRect::new(
+ (dst.x + self.view_offset.x) * self.view_scale.x,
+ (dst.y + self.view_offset.y) * self.view_scale.y,
+ dst.w * self.view_scale.x,
+ dst.h * self.view_scale.y,
+ ),
+ })
+ }
+
+ pub fn submit(&mut self, canvas: &mut Canvas<Window>) {
+ self.sprites.sort();
+ for SpriteDraw { src, dst, .. } in self.sprites.drain(..) {
+ canvas.copy_f(&self.texture, src, dst).unwrap();
+ }
+ }
+}
+
+impl Ord for SpriteDraw {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.z_order.cmp(&other.z_order)
+ }
+}
+impl PartialOrd for SpriteDraw {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(&other))
+ }
+}
+impl Eq for SpriteDraw {}
+impl PartialEq for SpriteDraw {
+ fn eq(&self, other: &Self) -> bool {
+ self.z_order == other.z_order && self.src == other.src && self.dst == other.dst
+ }
+}