diff options
| author | Freya Murphy <freya@freyacat.org> | 2025-11-06 21:01:34 -0500 |
|---|---|---|
| committer | Freya Murphy <freya@freyacat.org> | 2025-11-06 21:09:00 -0500 |
| commit | 73c4521b723129dff2d7c4ccac4d48dd16eae2b3 (patch) | |
| tree | 7f188f740bed947a34b380d8234c7f1e8a2eb170 /graphics/src/render.rs | |
| parent | Light bsp refactoring (diff) | |
| download | DungeonCrawl-73c4521b723129dff2d7c4ccac4d48dd16eae2b3.tar.gz DungeonCrawl-73c4521b723129dff2d7c4ccac4d48dd16eae2b3.tar.bz2 DungeonCrawl-73c4521b723129dff2d7c4ccac4d48dd16eae2b3.zip | |
graphics: refactor renderer
Diffstat (limited to '')
| -rw-r--r-- | graphics/src/render.rs | 604 |
1 files changed, 304 insertions, 300 deletions
diff --git a/graphics/src/render.rs b/graphics/src/render.rs index 4166a7a..2467bb1 100644 --- a/graphics/src/render.rs +++ b/graphics/src/render.rs @@ -1,30 +1,78 @@ -//! The `render` module contains the structures for displaying -//! the game, with each frame represented by a `Renderer` and -//! frame specific information in `FrameInfo`. - -use std::{cell::RefCell, ops::Div, rc::Rc}; +use std::ops::Div; use dungeon::{ - Direction, Dungeon, Entity, EntityKind, FPos, Floor, MAP_SIZE, PLAYER_FULL_HEALTH, - Player, Pos, Tile, + Direction, Dungeon, Entity, EntityKind, FPos, Floor, Item, MAP_SIZE, + PLAYER_FULL_HEALTH, Player, Pos, Tile, }; use raylib::{ - RaylibThread, + RaylibHandle, RaylibThread, camera::Camera2D, color::Color, math::{Rectangle, Vector2}, - prelude::{ - RaylibDraw, RaylibDrawHandle, RaylibHandle, RaylibMode2D, RaylibMode2DExt, - RaylibTextureModeExt, - }, - texture::{RaylibTexture2D, RenderTexture2D}, + prelude::{RaylibDraw, RaylibMode2DExt, RaylibTextureModeExt}, + texture::{RaylibTexture2D, RenderTexture2D, Texture2D}, }; -use crate::assets::{AtlasTexture, BASE_TILE_SIZE, ImageData, WALL_HEIGHT}; +/// The baseline size of all ingame sprites and tile textures +const BASE_TILE_SIZE: i32 = 32; + +/// The height of the wall (offset between tile layers) +const WALL_HEIGHT: i32 = 13; /// The (prefered) view distance of the game const VIEW_DISTANCE: i32 = 5; +#[derive(Debug)] +struct Textures { + // Tilemap + atlas: Texture2D, + // UI + heart_full: Texture2D, + heart_half: Texture2D, + heart_empty: Texture2D, + // Misc + error: Texture2D, +} +impl Textures { + fn new(handle: &mut RaylibHandle, thread: &RaylibThread) -> crate::Result<Self> { + let atlas = handle.load_texture(thread, "assets/atlas.bmp")?; + let heart_full = handle.load_texture(thread, "assets/heart_full.bmp")?; + let heart_half = handle.load_texture(thread, "assets/heart_half.bmp")?; + let heart_empty = handle.load_texture(thread, "assets/heart_empty.bmp")?; + let error = handle.load_texture(thread, "assets/error.bmp")?; + + Ok(Self { + atlas, + heart_full, + heart_half, + heart_empty, + error, + }) + } + + fn item_texture(&self, _item: &Item) -> &Texture2D { + // TODO: make item textures + &self.error + } +} + +// Tile atlas.bmp textures +const ATLAS_WALL_SIDE: (i32, i32) = (0, 0); +const ATLAS_FLOOR_FULL: (i32, i32) = (1, 0); +const ATLAS_FLOOR_EMPTY: (i32, i32) = (2, 0); +const ATLAS_WALL_TOP: (i32, i32) = (3, 0); +const ATLAS_WALL_EDGE: (i32, i32) = (0, 1); + +// Entity atlas.bmp textures +const ATLAS_PLAYER: (i32, i32) = (0, 2); + +// UI atlas.bmp textures +const ATLAS_INV_TOP_LAYER: (i32, i32) = (1, 2); +const ATLAS_INV_BOTTOM_LAYER: (i32, i32) = (2, 2); + +// Misc atlas.bmp textures +const ATLAS_ERROR: (i32, i32) = (3, 3); + /// Full source rec for any texture const FULL_SOURCE_REC: Rectangle = Rectangle { x: 0.0, @@ -33,208 +81,131 @@ const FULL_SOURCE_REC: Rectangle = Rectangle { height: BASE_TILE_SIZE as f32, }; -/// The `FrameInfo` struct stores information used during a single frame -#[derive(Clone, Copy, Debug)] -struct FrameInfo { - /// The last calculated fps +#[derive(Debug)] +pub struct Renderer { + /* Persistant Render Data */ + /// All loaded image resources/textures + textures: Textures, + /// Top layer of the tilemap (walls) + tilemap_fg: RenderTexture2D, + /// Bottom layer of the tilemap (floor and half-height walls) + tilemap_bg: RenderTexture2D, + /// Last computed hash of the tilemap (floor) + tiles_hash: Option<u64>, + /* Per Frame Caculated Data */ + /// Current tile size we are rendering + tile_size: i32, + /// Last known FPS fps: u32, - /// The render width of the framebuffer in pixels + /// Render width of the current frame width: i32, - /// The render height of the framebuffer in pixels + /// Render height of the current frame height: i32, - /// The tile size in pixels - tile_size: i32, -} -impl FrameInfo { - fn new(handle: &RaylibHandle) -> Self { - let fps = handle.get_fps(); - let width = handle.get_render_width(); - let height = handle.get_render_height(); - let tile_size = { - let size = width.min(height); - let dist = VIEW_DISTANCE * 2 + 1; - let pixels = size.div(dist).max(BASE_TILE_SIZE); - 1 << (i32::BITS - pixels.leading_zeros()) - }; - - Self { - fps, - width, - height, - tile_size, - } - } -} - -/// The `State` struct stores persistant renderer data used across multiple frames -#[derive(Debug)] -struct State { - /// Set of sprites to be drawn - image: ImageData, - /// Top layer of the tile map (used for back and top sides of walls) - tiles_tex_fg: RefCell<RenderTexture2D>, - /// Bottom layer of the tile map (used for most things) - tiles_tex_bg: RefCell<RenderTexture2D>, - /// Hash of tiles used to draw the tile textures - tiles_hash: RefCell<u64>, } -impl State { - fn new( - handle: &mut RaylibHandle, - thread: &RaylibThread, - image: ImageData, - ) -> crate::Result<Self> { +impl Renderer { + pub fn new(handle: &mut RaylibHandle, thread: &RaylibThread) -> crate::Result<Self> { + // Load resources + let textures = Textures::new(handle, thread)?; + // Load render textures let tex_size = MAP_SIZE as u32 * BASE_TILE_SIZE as u32; - let tiles_tex_fg = handle.load_render_texture(thread, tex_size, tex_size)?; - let tiles_tex_bg = handle.load_render_texture(thread, tex_size, tex_size)?; + let tilemap_fg = handle.load_render_texture(thread, tex_size, tex_size)?; + let tilemap_bg = handle.load_render_texture(thread, tex_size, tex_size)?; Ok(Self { - image, - tiles_tex_fg: RefCell::new(tiles_tex_fg), - tiles_tex_bg: RefCell::new(tiles_tex_bg), - tiles_hash: RefCell::new(0), + textures, + tilemap_fg, + tilemap_bg, + tiles_hash: None, + tile_size: 0, + fps: 0, + width: 0, + height: 0, }) } -} -/// The `Renderer` struct is the persistant renderer -/// for the duration for the application. -#[derive(Debug)] -pub struct Renderer { - /// Persistant render state - state: Rc<State>, -} -impl Renderer { - pub(crate) fn new( + pub fn draw_frame( + &mut self, handle: &mut RaylibHandle, - thread: &RaylibThread, - image: ImageData, - ) -> crate::Result<Self> { - let state = Rc::new(State::new(handle, thread, image)?); - - Ok(Self { state }) + t: &RaylibThread, + dungeon: &Dungeon, + ) { + self.update_per_frame_data(handle); + let mut r = handle.begin_drawing(t); + r.clear_background(Color::BLACK); + self.draw_dungeon(&mut r, t, dungeon); + self.draw_ui(&mut r, dungeon); } - /// Invokes the renderer for the current frame - pub(crate) fn invoke<'a>( - &'a mut self, - handle: &'a mut RaylibHandle, - thread: &'a RaylibThread, - ) -> FrameRendererImpl<'a> { - let info = FrameInfo::new(handle); - let state = Rc::clone(&self.state); - FrameRenderer { - handle: handle.begin_drawing(thread), - thread, - info, - state, - } - } -} - -pub type FrameRendererImpl<'a> = FrameRenderer<'a, RaylibDrawHandle<'a>>; - -macro_rules! tex_renderer { - ($fr:expr, $tex:expr) => { - FrameRenderer { - handle: $fr.handle.begin_texture_mode($fr.thread, $tex), - thread: &$fr.thread, - info: $fr.info, - state: Rc::clone(&$fr.state), - } - }; -} - -pub struct FrameRenderer<'a, T> -where - T: RaylibDraw, -{ - /// The current draw handle for raylib - handle: T, - /// The raylib thread - thread: &'a RaylibThread, - /// Non drawing information for this current frame - info: FrameInfo, - /// Persistant render state - state: Rc<State>, -} -impl<'a, T> FrameRenderer<'a, T> -where - T: RaylibDraw + RaylibMode2DExt + RaylibTextureModeExt, -{ - /// Draws an entire frame - pub fn draw_frame(&mut self, dungeon: &Dungeon) { - self.clear(Color::BLACK); - self.draw_dungeon(dungeon); - self.draw_ui(dungeon); + fn update_per_frame_data(&mut self, handle: &RaylibHandle) { + // Get last known fps + self.fps = handle.get_fps(); + // Get size (in pixels) to draw each tile + self.width = handle.get_render_width(); + self.height = handle.get_render_height(); + self.tile_size = { + let size = self.width.min(self.height); + let dist = VIEW_DISTANCE * 2 + 1; + let pixels = size.div(dist).max(BASE_TILE_SIZE); + 1 << (i32::BITS - pixels.leading_zeros()) + }; } - /// Draws the dungeon, (tiles and entities) - pub fn draw_dungeon(&mut self, dungeon: &Dungeon) { - self.update_tilemaps(&dungeon.floor); - let camera = dungeon.camera(); - let mut renderer = self.camera_renderer(camera); - renderer.draw_tiles_bg(); - renderer.draw_entities(dungeon); - renderer.draw_tiles_fg(); - } -} -impl<'a, T> FrameRenderer<'a, T> -where - T: RaylibDraw + RaylibMode2DExt, -{ - /// Returns a raylib camera for the given position - #[must_use] - fn camera_renderer<'b>( - &'b mut self, - cpos: FPos, - ) -> FrameRenderer<'b, RaylibMode2D<'b, T>> { - let width = self.info.width; - let height = self.info.height; - let camera = Camera2D { + fn render_camera(&self, dungeon: &Dungeon) -> Camera2D { + let cpos = dungeon.camera(); + let width = self.width; + let height = self.height; + Camera2D { target: Vector2::from(cpos.xy()) - .scale_by(self.info.tile_size as f32) + .scale_by(self.tile_size as f32) .max(Vector2::new(width as f32 / 2.0, height as f32 / 2.0)) .min(Vector2::new( - (MAP_SIZE as i32 * self.info.tile_size) as f32 - (width as f32 / 2.0), - (MAP_SIZE as i32 * self.info.tile_size) as f32 - (height as f32 / 2.0), + (MAP_SIZE as i32 * self.tile_size) as f32 - (width as f32 / 2.0), + (MAP_SIZE as i32 * self.tile_size) as f32 - (height as f32 / 2.0), )), offset: Vector2::new(width as f32 / 2.0, height as f32 / 2.0), rotation: 0.0, zoom: 1.0, - }; - let handle = self.handle.begin_mode2D(camera); - FrameRenderer { - handle, - thread: self.thread, - info: self.info, - state: Rc::clone(&self.state), } } -} -impl<'a, T> FrameRenderer<'a, T> -where - T: RaylibDraw + RaylibTextureModeExt, -{ + + fn draw_dungeon<R>(&mut self, r: &mut R, t: &RaylibThread, dungeon: &Dungeon) + where + R: RaylibDraw + RaylibMode2DExt + RaylibTextureModeExt, + { + self.update_tilemaps(r, t, &dungeon.floor); + let camera = self.render_camera(dungeon); + let mut rc = r.begin_mode2D(camera); + self.draw_tiles_bg(&mut rc); + self.draw_entities(&mut rc, dungeon); + self.draw_tiles_fg(&mut rc); + } + /// Updates all tilemaps - fn update_tilemaps(&mut self, floor: &Floor) { - let hash = floor.hash(); - let old_hash = std::mem::replace(&mut *self.state.tiles_hash.borrow_mut(), hash); - if old_hash == hash { + fn update_tilemaps<R>(&mut self, r: &mut R, t: &RaylibThread, floor: &Floor) + where + R: RaylibDraw + RaylibTextureModeExt, + { + let current_hash = floor.hash(); + if let Some(old_hash) = self.tiles_hash + && old_hash == current_hash + { // Textures are up to date return; - } + }; + self.tiles_hash = Some(current_hash); - self.update_fg_tilemap(floor); - self.update_bg_tilemap(floor); + self.update_fg_tilemap(r, t, floor); + self.update_bg_tilemap(r, t, floor); } /// Draws the foregound tile map - fn update_fg_tilemap(&mut self, floor: &Floor) { + fn update_fg_tilemap<R>(&mut self, r: &mut R, t: &RaylibThread, floor: &Floor) + where + R: RaylibDraw + RaylibTextureModeExt, + { let size = BASE_TILE_SIZE as f32; - let tex = &mut self.state.tiles_tex_fg.borrow_mut(); - let mut renderer = tex_renderer!(self, tex); - renderer.clear(Color::BLANK); + let mut rt = r.begin_texture_mode(t, &mut self.tilemap_fg); + rt.clear_background(Color::BLANK); for pos in Pos::values() { let (fx, fy) = FPos::from(pos).xy(); @@ -246,86 +217,138 @@ where }; // draw base wall top texture - renderer.draw_atlas(AtlasTexture::WallBase, xs, ys, size, 0.0); + rt.draw_atlas(&self.textures.atlas, ATLAS_WALL_TOP, xs, ys, size, 0.0); // draw top wall borders let is_wall = |dir| pos.step(dir).map_or(Tile::Wall, |p| floor.get(p)).is_wall(); if !is_wall(Direction::North) { - renderer.draw_atlas(AtlasTexture::WallEdgeNorth, xs, ys, size, 0.0); + rt.draw_atlas(&self.textures.atlas, ATLAS_WALL_EDGE, xs, ys, size, 0.0); } if !is_wall(Direction::East) { - renderer.draw_atlas(AtlasTexture::WallEdgeEast, xs, ys, size, 0.0); + rt.draw_atlas(&self.textures.atlas, ATLAS_WALL_EDGE, xs, ys, size, 90.0); } if !is_wall(Direction::South) { - renderer.draw_atlas(AtlasTexture::WallEdgeSouth, xs, ys, size, 0.0); + rt.draw_atlas(&self.textures.atlas, ATLAS_WALL_EDGE, xs, ys, size, 180.0); } if !is_wall(Direction::West) { - renderer.draw_atlas(AtlasTexture::WallEdgeWest, xs, ys, size, 0.0); + rt.draw_atlas(&self.textures.atlas, ATLAS_WALL_EDGE, xs, ys, size, 270.0); } } } /// Draws the foregound tile map - fn update_bg_tilemap(&mut self, floor: &Floor) { + fn update_bg_tilemap<R>(&mut self, r: &mut R, t: &RaylibThread, floor: &Floor) + where + R: RaylibDraw + RaylibTextureModeExt, + { let size = BASE_TILE_SIZE as f32; - let tex = &mut self.state.tiles_tex_bg.borrow_mut(); - let mut renderer = tex_renderer!(self, tex); - renderer.clear(Color::BLACK); + let mut rt = r.begin_texture_mode(t, &mut self.tilemap_bg); + rt.clear_background(Color::BLACK); for pos in Pos::values() { let (x, y) = pos.xy(); let tile = floor.get(pos); - let tex = match tile { - Tile::Wall => AtlasTexture::Wall, - Tile::Air if (x + y) % 2 == 0 => AtlasTexture::FloorFull, - Tile::Air if (x + y) % 2 == 1 => AtlasTexture::FloorEmpty, - _ => AtlasTexture::Error, + let idx = match tile { + Tile::Wall => ATLAS_WALL_SIDE, + Tile::Air if (x + y) % 2 == 0 => ATLAS_FLOOR_FULL, + Tile::Air if (x + y) % 2 == 1 => ATLAS_FLOOR_EMPTY, + _ => ATLAS_ERROR, }; - renderer.draw_atlas(tex, x as f32 * size, y as f32 * size, size, 0.0); + rt.draw_atlas( + &self.textures.atlas, + idx, + x as f32 * size, + y as f32 * size, + size, + 0.0, + ); } } -} -impl<'a, T> FrameRenderer<'a, T> -where - T: RaylibDraw, -{ - /// Clear the screen - pub fn clear(&mut self, color: Color) { - self.handle.clear_background(color); + + /// Draw dungeon tiles (foreground layer) + fn draw_tiles_fg<R>(&mut self, r: &mut R) + where + R: RaylibDraw, + { + let size = self.tile_size as f32; + let offset = WALL_HEIGHT as f32; + r.draw_tilemap(&self.tilemap_fg, size, offset); + } + + /// Draw dungeon tiles (background layer) + fn draw_tiles_bg<R>(&mut self, r: &mut R) + where + R: RaylibDraw, + { + let size = self.tile_size as f32; + let offset = 0.0; + r.draw_tilemap(&self.tilemap_bg, size, offset); + } + + /// Draws the entities on the map + fn draw_entities<R>(&mut self, r: &mut R, dungeon: &Dungeon) + where + R: RaylibDraw, + { + self.draw_entity(r, &dungeon.player.entity); + for enemy in &dungeon.enemies { + self.draw_entity(r, &enemy.entity); + } + } + + /// Draws an entity + fn draw_entity<R>(&self, r: &mut R, entity: &Entity) + where + R: RaylibDraw, + { + let size = self.tile_size as f32; + let x = entity.fpos.x(); + let y = entity.fpos.y(); + let tex = match entity.kind { + EntityKind::Player => ATLAS_PLAYER, + _ => ATLAS_ERROR, + }; + r.draw_atlas(&self.textures.atlas, tex, x * size, y * size, size, 0.0); } /// Draws player HP, inventory, and floor number - pub fn draw_ui(&mut self, dungeon: &Dungeon) { + fn draw_ui<R>(&self, r: &mut R, dungeon: &Dungeon) + where + R: RaylibDraw, + { // Draw core ui components - self.draw_health(&dungeon.player); - self.draw_inventory(&dungeon.player); + self.draw_health(r, &dungeon.player); + self.draw_inventory(r, &dungeon.player); // Draw debug info #[cfg(feature = "debug")] - self.draw_debug_ui(dungeon); + self.draw_debug_ui(r); } - fn draw_health(&mut self, player: &Player) { + fn draw_health<R>(&self, r: &mut R, player: &Player) + where + R: RaylibDraw, + { let health = player.entity.health.unwrap_or(0); let hearts = PLAYER_FULL_HEALTH.div_ceil(2); let mut full_hearts = health / 2; let mut half_hearts = health % 2; let mut empty_hearts = hearts.saturating_sub(full_hearts + half_hearts); - let size = self.info.tile_size.div(2).max(BASE_TILE_SIZE); + let size = self.tile_size.div(2).max(BASE_TILE_SIZE); let y = BASE_TILE_SIZE; let mut x = BASE_TILE_SIZE; loop { let tex = if full_hearts > 0 { full_hearts -= 1; - &self.state.image.heart_full + &self.textures.heart_full } else if half_hearts > 0 { half_hearts -= 1; - &self.state.image.heart_half + &self.textures.heart_half } else if empty_hearts > 0 { empty_hearts -= 1; - &self.state.image.heart_empty + &self.textures.heart_empty } else { break; }; @@ -336,7 +359,7 @@ where width: size as f32, height: size as f32, }; - self.handle.draw_texture_pro( + r.draw_texture_pro( tex, FULL_SOURCE_REC, dest_rec, @@ -348,15 +371,19 @@ where } } - fn draw_inventory(&mut self, player: &Player) { + fn draw_inventory<R>(&self, r: &mut R, player: &Player) + where + R: RaylibDraw, + { let len = i32::try_from(player.inventory.len()).unwrap_or(0); // size of the inv blocks - let size = self.info.tile_size.div(2).max(BASE_TILE_SIZE); + let size = self.tile_size.div(2).max(BASE_TILE_SIZE); + let half_size = size as f32 / 2.0; // position of the inv blocks - let y = self.info.height - size; - let mut x = self.info.width / 2 - (size * len / 2); + let y = self.height - size; + let mut x = self.width / 2 - (size * len / 2); // size of font for number index let font_size = size / 3; @@ -369,14 +396,16 @@ where width: size as f32, height: size as f32, }; - self.draw_ui_atlas( - AtlasTexture::InvBottomLayer, - x as f32, - y as f32, + r.draw_atlas( + &self.textures.atlas, + ATLAS_INV_BOTTOM_LAYER, + x as f32 + half_size, + y as f32 + half_size, size as f32, + 0.0, ); - let tex = self.state.image.get_item_texture(item); - self.handle.draw_texture_pro( + let tex = self.textures.item_texture(item); + r.draw_texture_pro( tex, FULL_SOURCE_REC, dest_rec, @@ -384,8 +413,15 @@ where 0.0, Color::WHITE, ); - self.draw_ui_atlas(AtlasTexture::InvTopLayer, x as f32, y as f32, size as f32); - self.handle.draw_text( + r.draw_atlas( + &self.textures.atlas, + ATLAS_INV_TOP_LAYER, + x as f32 + half_size, + y as f32 + half_size, + size as f32, + 0.0, + ); + r.draw_text( &format!("{}", idx + 1), x + font_offset + font_offset / 5, y + font_offset, @@ -398,63 +434,54 @@ where /// Draws debug information ontop of other UI elements /// Called by default if `debug` featue is enabled - pub fn draw_debug_ui(&mut self, _dungeon: &Dungeon) { - self.draw_fps(); + fn draw_debug_ui<R>(&self, r: &mut R) + where + R: RaylibDraw, + { + self.draw_fps(r); } /// Draw FPS counter (debug) - fn draw_fps(&mut self) { - let fps_str = format!("{}", self.info.fps); - self.handle.draw_text(&fps_str, 10, 10, 30, Color::YELLOW); - } - - /// Draws the entities on the map - fn draw_entities(&mut self, dungeon: &Dungeon) { - self.draw_entity(&dungeon.player.entity); - for enemy in &dungeon.enemies { - self.draw_entity(&enemy.entity); - } - } - - /// Draws an entity - fn draw_entity(&mut self, entity: &Entity) { - let size = self.info.tile_size as f32; - let x = entity.fpos.x(); - let y = entity.fpos.y(); - let tex = match entity.kind { - EntityKind::Player => AtlasTexture::Player, - _ => AtlasTexture::Error, - }; - self.draw_atlas(tex, x * size, y * size, size, 0.0); - } - - /// Draw dungeon tiles (background layer) - fn draw_tiles_bg(&mut self) { - let tex = &*self.state.tiles_tex_bg.borrow(); - let size = self.info.tile_size as f32; - let offset = 0.0; - self.handle.draw_tilemap(tex, size, offset); + fn draw_fps<R>(&self, r: &mut R) + where + R: RaylibDraw, + { + let fps_str = format!("{}", self.fps); + r.draw_text(&fps_str, 10, 10, 30, Color::YELLOW); } +} - /// Draw dungeon tiles (foreground layer) - fn draw_tiles_fg(&mut self) { - let tex = &*self.state.tiles_tex_fg.borrow(); - let size = self.info.tile_size as f32; - let offset = WALL_HEIGHT as f32; - self.handle.draw_tilemap(tex, size, offset); +trait Vector2Ext { + fn min(self, other: Self) -> Self; + fn max(self, other: Self) -> Self; +} +impl Vector2Ext for Vector2 { + fn min(self, other: Self) -> Self { + Self::new(self.x.min(other.x), self.y.min(other.y)) } - /// Draw an atlas texture index (with ui offset applied) - fn draw_ui_atlas(&mut self, tex: AtlasTexture, x: f32, y: f32, size: f32) { - let half_size = size / 2.0; - self.draw_atlas(tex, x + half_size, y + half_size, size, 0.0); + fn max(self, other: Self) -> Self { + Self::new(self.x.max(other.x), self.y.max(other.y)) } +} +trait RaylibDrawExt +where + Self: RaylibDraw, +{ /// Draw an atlas texture index - fn draw_atlas(&mut self, tex: AtlasTexture, x: f32, y: f32, size: f32, rotation: f32) { + fn draw_atlas( + &mut self, + tex: &Texture2D, + (ax, ay): (i32, i32), + x: f32, + y: f32, + size: f32, + rotation: f32, + ) { let source_rec = Rectangle { - x: (tex.x() * BASE_TILE_SIZE) as f32, - y: (tex.y() * BASE_TILE_SIZE) as f32, + x: (ax * BASE_TILE_SIZE) as f32, + y: (ay * BASE_TILE_SIZE) as f32, width: BASE_TILE_SIZE as f32, height: BASE_TILE_SIZE as f32, }; @@ -468,32 +495,9 @@ where x: dest_rec.width / 2.0, y: dest_rec.height / 2.0, }; - self.handle.draw_texture_pro( - &self.state.image.atlas, - source_rec, - dest_rec, - origin, - rotation, - Color::WHITE, - ); - } -} - -trait Vector2Ext { - fn min(self, other: Self) -> Self; - fn max(self, other: Self) -> Self; -} -impl Vector2Ext for Vector2 { - fn min(self, other: Self) -> Self { - Self::new(self.x.min(other.x), self.y.min(other.y)) + self.draw_texture_pro(tex, source_rec, dest_rec, origin, rotation, Color::WHITE); } - fn max(self, other: Self) -> Self { - Self::new(self.x.max(other.x), self.y.max(other.y)) - } -} - -trait RaylibDrawExt: RaylibDraw { /// Draw dungeon tiles helper function fn draw_tilemap(&mut self, tex: &RenderTexture2D, size: f32, offset: f32) { let scale = size / BASE_TILE_SIZE as f32; @@ -515,4 +519,4 @@ trait RaylibDrawExt: RaylibDraw { self.draw_texture_pro(tex, source_rec, dest_rec, origin, 0.0, Color::WHITE); } } -impl<T: RaylibDraw> RaylibDrawExt for T {} +impl<T> RaylibDrawExt for T where T: RaylibDraw {} |