feat: atlas tile color modulation

This commit is contained in:
2025-07-26 15:06:27 -05:00
parent 9066b2cdbc
commit a1d37a1a0b
12 changed files with 80 additions and 78 deletions

View File

@@ -1,6 +1,6 @@
//! This module provides a simple animation and atlas system for textures.
use anyhow::Result;
use sdl2::render::WindowCanvas;
use sdl2::{pixels::Color, render::WindowCanvas};
use crate::texture::sprite::AtlasTile;
@@ -34,16 +34,16 @@ impl AnimatedTexture {
self.ticker += 1;
}
pub fn current_tile(&self) -> &AtlasTile {
pub fn current_tile(&mut self) -> &mut AtlasTile {
if self.ticks_per_frame == 0 {
return &self.frames[0];
return &mut self.frames[0];
}
let frame_index = (self.ticker / self.ticks_per_frame) as usize % self.frames.len();
&self.frames[frame_index]
&mut self.frames[frame_index]
}
pub fn render(&self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect) -> Result<()> {
let tile = self.current_tile();
pub fn render(&mut self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect) -> Result<()> {
let mut tile = self.current_tile();
tile.render(canvas, dest)
}
}

View File

@@ -38,7 +38,7 @@ impl BlinkingTexture {
}
/// Renders the blinking texture.
pub fn render(&self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect) -> Result<()> {
pub fn render(&mut self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect) -> Result<()> {
if self.visible {
self.animation.render(canvas, dest)
} else {

View File

@@ -37,28 +37,28 @@ impl DirectionalAnimatedTexture {
pub fn render(&mut self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect, direction: Direction) -> Result<()> {
let frames = match direction {
Direction::Up => &self.up,
Direction::Down => &self.down,
Direction::Left => &self.left,
Direction::Right => &self.right,
Direction::Up => &mut self.up,
Direction::Down => &mut self.down,
Direction::Left => &mut self.left,
Direction::Right => &mut self.right,
};
let frame_index = (self.ticker / self.ticks_per_frame) as usize % frames.len();
let tile = &frames[frame_index];
let tile = &mut frames[frame_index];
tile.render(canvas, dest)
}
pub fn render_stopped(&mut self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect, direction: Direction) -> Result<()> {
let frames = match direction {
Direction::Up => &self.up,
Direction::Down => &self.down,
Direction::Left => &self.left,
Direction::Right => &self.right,
Direction::Up => &mut self.up,
Direction::Down => &mut self.down,
Direction::Left => &mut self.left,
Direction::Right => &mut self.right,
};
// Show the last frame (full sprite) when stopped
let tile = &frames[1];
let tile = &mut frames[1];
tile.render(canvas, dest)
}

View File

@@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::rc::Rc;
use crate::texture::sprite::{AtlasTile, SpriteAtlas};
@@ -8,6 +9,6 @@ pub mod directional;
pub mod sprite;
pub mod text;
pub fn get_atlas_tile(atlas: &Rc<SpriteAtlas>, name: &str) -> AtlasTile {
pub fn get_atlas_tile(atlas: &Rc<RefCell<SpriteAtlas>>, name: &str) -> AtlasTile {
SpriteAtlas::get_tile(atlas, name).unwrap_or_else(|| panic!("Could not find tile {}", name))
}

View File

@@ -1,8 +1,10 @@
use anyhow::Result;
use glam::U16Vec2;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Canvas, RenderTarget, Texture};
use serde::Deserialize;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
@@ -21,15 +23,30 @@ pub struct MapperFrame {
#[derive(Clone)]
pub struct AtlasTile {
pub atlas: Rc<SpriteAtlas>,
pub atlas: Rc<RefCell<SpriteAtlas>>,
pub pos: U16Vec2,
pub size: U16Vec2,
pub color: Option<Color>,
}
impl AtlasTile {
pub fn render<C: RenderTarget>(&self, canvas: &mut Canvas<C>, dest: Rect) -> Result<()> {
pub fn render<C: RenderTarget>(&mut self, canvas: &mut Canvas<C>, dest: Rect) -> Result<()> {
let color = self
.color
.unwrap_or(self.atlas.borrow().default_color.unwrap_or(Color::WHITE));
self.render_with_color(canvas, dest, color)
}
pub fn render_with_color<C: RenderTarget>(&mut self, canvas: &mut Canvas<C>, dest: Rect, color: Color) -> Result<()> {
let src = Rect::new(self.pos.x as i32, self.pos.y as i32, self.size.x as u32, self.size.y as u32);
canvas.copy(&self.atlas.texture, src, dest).map_err(anyhow::Error::msg)?;
let mut atlas = self.atlas.borrow_mut();
if atlas.last_modulation != Some(color) {
atlas.texture.set_color_mod(color.r, color.g, color.b);
atlas.last_modulation = Some(color);
}
canvas.copy(&atlas.texture, src, dest).map_err(anyhow::Error::msg)?;
Ok(())
}
}
@@ -37,6 +54,8 @@ impl AtlasTile {
pub struct SpriteAtlas {
texture: Texture<'static>,
tiles: HashMap<String, MapperFrame>,
default_color: Option<Color>,
last_modulation: Option<Color>,
}
impl SpriteAtlas {
@@ -44,17 +63,25 @@ impl SpriteAtlas {
Self {
texture,
tiles: mapper.frames,
default_color: None,
last_modulation: None,
}
}
pub fn get_tile(atlas: &Rc<SpriteAtlas>, name: &str) -> Option<AtlasTile> {
atlas.tiles.get(name).map(|frame| AtlasTile {
atlas: atlas.clone(),
pub fn get_tile(atlas: &Rc<RefCell<SpriteAtlas>>, name: &str) -> Option<AtlasTile> {
let atlas_ref = atlas.borrow();
atlas_ref.tiles.get(name).map(|frame| AtlasTile {
atlas: Rc::clone(atlas),
pos: U16Vec2::new(frame.x, frame.y),
size: U16Vec2::new(frame.width, frame.height),
color: None,
})
}
pub fn set_color(&mut self, color: Color) {
self.default_color = Some(color);
}
pub fn texture(&self) -> &Texture<'static> {
&self.texture
}

View File

@@ -48,8 +48,10 @@
use anyhow::Result;
use glam::UVec2;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Canvas, RenderTarget};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
@@ -57,14 +59,14 @@ use crate::texture::sprite::{AtlasTile, SpriteAtlas};
/// A text texture that renders characters from the atlas.
pub struct TextTexture {
atlas: Rc<SpriteAtlas>,
atlas: Rc<RefCell<SpriteAtlas>>,
char_map: HashMap<char, AtlasTile>,
scale: f32,
}
impl TextTexture {
/// Creates a new text texture with the given atlas and scale.
pub fn new(atlas: Rc<SpriteAtlas>, scale: f32) -> Self {
pub fn new(atlas: Rc<RefCell<SpriteAtlas>>, scale: f32) -> Self {
Self {
atlas,
char_map: HashMap::new(),
@@ -107,13 +109,13 @@ impl TextTexture {
}
/// Renders a string of text at the given position.
pub fn render<C: RenderTarget>(&mut self, canvas: &mut Canvas<C>, text: &str, position: UVec2) -> Result<()> {
pub fn render<C: RenderTarget>(&mut self, canvas: &mut Canvas<C>, text: &str, position: UVec2, color: Color) -> Result<()> {
let mut x_offset = 0;
let char_width = (8.0 * self.scale) as u32;
let char_height = (8.0 * self.scale) as u32;
for c in text.chars() {
if let Some(tile) = self.get_char_tile(c) {
if let Some(mut tile) = self.get_char_tile(c) {
let dest = sdl2::rect::Rect::new((position.x + x_offset) as i32, position.y as i32, char_width, char_height);
tile.render(canvas, dest)?;
}