Files
mhlib/rust/src/godot/sprites.rs
2023-05-04 00:32:54 +02:00

134 lines
4.3 KiB
Rust

use crate::formats::sprites::{CropMode, RenderMode, Sprites};
use godot::builtin::{GodotString, Rect2, StringName, ToVariant, Vector2};
use godot::engine::utilities::printerr;
use godot::engine::{
load, AtlasTexture, ImageTexture, PlaceholderTexture2D, ResourceLoader, SpriteFrames,
};
use godot::obj::{Gd, Share};
use godot::prelude::GodotClass;
const FPS: f64 = 15.0;
const SPRITE_EXTENSIONS: &[&str] = &["bmp", "rle"];
pub fn load_sprite_frames(sprites: Vec<Sprites>, path: GodotString) -> Gd<SpriteFrames> {
let dir = path
.to_string()
.strip_suffix("/sprites.txt")
.unwrap()
.to_string();
let mut sprite_frames = SpriteFrames::new();
for sprite in sprites.into_iter() {
if let RenderMode::FlipX = sprite.render_mode {
continue;
}
sprite_frames.add_animation(StringName::from(&sprite.name));
sprite_frames.set_animation_speed(StringName::from(&sprite.name), FPS);
match select_from_extensions(&dir, &sprite.file_name) {
Some((path, "rle")) => extract_rle_frames(&mut sprite_frames, &sprite, path),
Some((path, "bmp")) => extract_bitmap_frames(&mut sprite_frames, &sprite, path),
Some(_) | None => {
printerr(
format!("Missing sprite '{}'", sprite.file_name).to_variant(),
&[],
);
let texture = PlaceholderTexture2D::new();
sprite_frames.add_frame(
StringName::from(&sprite.name),
texture.upcast(),
60.0 / FPS,
0,
);
}
}
}
sprite_frames
}
/// Loads an RLE file as SpriteFrames and extracts
/// its frames into `sprite_frames`
fn extract_rle_frames(sprite_frames: &mut SpriteFrames, sprite: &Sprites, path: String) {
let frames: Gd<SpriteFrames> = load(path);
for frame_idx in 0..frames.get_frame_count("default".into()) {
sprite_frames.add_frame(
StringName::from(&sprite.name),
frames
.get_frame_texture("default".into(), frame_idx)
.unwrap(),
60.0 / FPS,
0,
);
}
}
/// Loads a bitmap and extracts its frames into `sprite_frames`
/// creates an atlas if there are multiple frames.
fn extract_bitmap_frames(sprite_frames: &mut SpriteFrames, sprite: &Sprites, path: String) {
let texture: Gd<ImageTexture> = load(path);
let frame_count = if let Some(CropMode::FrameCount(frame_count)) = sprite.frames {
frame_count
} else {
1
};
if frame_count > 1 {
let height = texture.get_height();
let width = texture.get_width();
let frame_height = height / frame_count as i64;
for i in 0..frame_count as i64 {
let mut atlas = AtlasTexture::new();
atlas.set_atlas(texture.share().upcast());
atlas.set_region(Rect2 {
position: Vector2 {
x: 0.0,
y: (i * frame_height) as f32,
},
size: Vector2 {
x: width as f32,
y: frame_height as f32,
},
});
sprite_frames.add_frame(
StringName::from(&sprite.name),
atlas.upcast(),
60.0 / FPS,
0,
);
}
} else {
sprite_frames.add_frame(
StringName::from(&sprite.name),
texture.upcast(),
60.0 / FPS,
0,
);
}
}
/// Selects the extension based on which file exists
fn select_from_extensions(dir: &str, file_name: &str) -> Option<(String, &'static str)> {
SPRITE_EXTENSIONS
.iter()
.map(|ext| {
(
format!("{}/sprites/{}.{}", dir, file_name.to_lowercase(), ext),
*ext,
)
})
.find(|(path, ext)| {
ResourceLoader::singleton().exists(
path.clone().into(),
match *ext {
"rle" => SpriteFrames::CLASS_NAME.to_string(),
"bmp" => ImageTexture::CLASS_NAME.to_string(),
_ => panic!(),
}
.into(),
)
})
}