aboutsummaryrefslogtreecommitdiff
path: root/light-client/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-07-16 17:51:59 +0200
committermetamuffin <metamuffin@disroot.org>2024-07-16 17:51:59 +0200
commit9dfc5afb299ed74b277735bcf06f47b52f68caee (patch)
tree2a6cd5f5197e4d616c8907482698bfa781aab466 /light-client/src
parent14a4379a68f5e514ddeed82ef8f385d4acc9b4d1 (diff)
downloadhurrycurry-9dfc5afb299ed74b277735bcf06f47b52f68caee.tar
hurrycurry-9dfc5afb299ed74b277735bcf06f47b52f68caee.tar.bz2
hurrycurry-9dfc5afb299ed74b277735bcf06f47b52f68caee.tar.zst
a
Diffstat (limited to 'light-client/src')
-rw-r--r--light-client/src/main.rs1
-rw-r--r--light-client/src/render/mod.rs158
-rw-r--r--light-client/src/render/sprite.rs62
3 files changed, 221 insertions, 0 deletions
diff --git a/light-client/src/main.rs b/light-client/src/main.rs
index b00ba734..a1c02475 100644
--- a/light-client/src/main.rs
+++ b/light-client/src/main.rs
@@ -29,6 +29,7 @@ pub mod game;
pub mod network;
pub mod sprite_renderer;
pub mod tilemap;
+pub mod render;
fn main() {
env_logger::init_from_env("LOG");
diff --git a/light-client/src/render/mod.rs b/light-client/src/render/mod.rs
new file mode 100644
index 00000000..fbf3d7a5
--- /dev/null
+++ b/light-client/src/render/mod.rs
@@ -0,0 +1,158 @@
+/*
+ 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/>.
+
+*/
+pub mod sprite;
+
+use hurrycurry_protocol::glam::Vec2;
+use sdl2::{
+ pixels::PixelFormatEnum,
+ rect::{FRect, Rect},
+ render::{BlendMode, Canvas, Texture, TextureAccess, TextureCreator},
+ video::{Window, WindowContext},
+};
+use sprite::{Sprite, SpriteDraw};
+use std::collections::HashMap;
+
+pub struct SpriteRenderer<'a> {
+ metadata: HashMap<String, Rect>,
+
+ texture: Texture<'a>,
+
+ view_scale: Vec2,
+ view_offset: Vec2,
+
+ sprites: Vec<SpriteDraw>,
+}
+
+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(3.),
+ }
+ }
+
+ #[inline]
+ pub fn metadata(&self) -> &HashMap<String, Rect> {
+ &self.metadata
+ }
+
+ pub fn set_modulation(&mut self, r: u8, g: u8, b: u8, a: u8) {
+ self.texture.set_alpha_mod(a);
+ self.texture.set_color_mod(r, g, b);
+ }
+ pub fn reset_modulation(&mut self) {
+ self.set_modulation(255, 255, 255, 255)
+ }
+
+ pub fn draw_world(&mut self, sprite: SpriteDraw) {
+ self.sprites.push(SpriteDraw {
+ z_order: sprite.z_order,
+ src: sprite.src,
+ dst: FRect::new(
+ ((sprite.dst.x + self.view_offset.x) * 32.).round() * self.view_scale.x,
+ ((sprite.dst.y + self.view_offset.y) * 24.).round() * self.view_scale.y,
+ (sprite.dst.w * 32.).round() * self.view_scale.x,
+ (sprite.dst.h * 24.).round() * 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();
+ }
+ }
+}
+
+pub struct MiscTextures {
+ pub player: Sprite,
+}
+
+impl MiscTextures {
+ pub fn init(renderer: &SpriteRenderer) -> Self {
+ MiscTextures {
+ player: Sprite::new(
+ *renderer.metadata().get("player+a").unwrap(),
+ Vec2::Y * 0.3,
+ 0.,
+ ),
+ }
+ }
+}
diff --git a/light-client/src/render/sprite.rs b/light-client/src/render/sprite.rs
new file mode 100644
index 00000000..942db611
--- /dev/null
+++ b/light-client/src/render/sprite.rs
@@ -0,0 +1,62 @@
+use hurrycurry_protocol::glam::Vec2;
+use sdl2::rect::{FRect, Rect};
+
+pub struct Sprite {
+ z_offset: f32,
+ src: Rect,
+ relative_dst: FRect,
+}
+
+impl Sprite {
+ pub fn new(src: Rect, anchor: Vec2, elevation: f32) -> Self {
+ let relative_dst = FRect::new(
+ anchor.x - (src.w as f32) / 32. / 2.,
+ anchor.y - (src.h as f32) / 24.,
+ (src.w as f32) / 32.,
+ (src.h as f32) / 32.,
+ );
+ Self {
+ z_offset: -relative_dst.h + anchor.y - elevation,
+ src,
+ relative_dst,
+ }
+ }
+ pub fn new_tile(src: Rect) {
+ Self::new(src, Vec2::new(0.5, 1.0), 0.);
+ }
+ pub fn at(&self, pos: Vec2) -> SpriteDraw {
+ SpriteDraw {
+ z_order: ((self.z_offset + pos.y) * 24.) as i32,
+ src: self.src,
+ dst: FRect::new(
+ self.relative_dst.x + pos.x,
+ self.relative_dst.y + pos.y,
+ self.relative_dst.w,
+ self.relative_dst.h,
+ ),
+ }
+ }
+}
+
+pub struct SpriteDraw {
+ pub z_order: i32,
+ pub src: Rect,
+ pub dst: FRect,
+}
+
+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
+ }
+}