auto-gen spritesheets
@ -3,6 +3,7 @@ members = ["./game/game_logic", "./game/desktop_wrapper"]
|
||||
exclude = [
|
||||
"./third_party/raylib-rs/raylib",
|
||||
"./third_party/raylib-rs/raylib-sys",
|
||||
"./automation/anim_stitcher"
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
17
automation/anim_stitcher/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "anim_stitcher"
|
||||
publish = false
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[dependencies]
|
||||
serde = { version = "1.0.126", features = ["derive"] }
|
||||
serde_json = "1.0.64"
|
||||
build-helper = { version = "0.1.1", optional = true }
|
||||
image = { version = "0.24.1", optional = true }
|
||||
regex = { version = "1.5.5", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
generator = ["build-helper", "image", "regex"]
|
66
automation/anim_stitcher/src/generator.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use image::{GenericImage, ImageResult};
|
||||
use regex::Regex;
|
||||
|
||||
const SPRITE_TYPE_RE: &str = r"^([a-z]+)_";
|
||||
|
||||
fn sprite_name_to_output_dir(sprite_name: &str) -> PathBuf {
|
||||
// Determine the sprite type
|
||||
let sprite_type: String = Regex::new(SPRITE_TYPE_RE)
|
||||
.unwrap()
|
||||
.captures(sprite_name)
|
||||
.unwrap()
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.to_string()
|
||||
.into();
|
||||
|
||||
// Build the output directory
|
||||
Path::new("../dist/gen/anm/")
|
||||
.join(sprite_type)
|
||||
.join(sprite_name)
|
||||
}
|
||||
|
||||
pub fn stitch_sprites(sprite_name: &str, ordered_image_files: Vec<PathBuf>) -> ImageResult<()> {
|
||||
build_helper::warning!(
|
||||
"Generating sprite `{}` from {} images",
|
||||
sprite_name,
|
||||
ordered_image_files.len()
|
||||
);
|
||||
|
||||
// Collect all images into a vec of actual image data
|
||||
let mut images = Vec::new();
|
||||
for image_file in ordered_image_files {
|
||||
let image_data = image::open(image_file.as_path()).unwrap();
|
||||
images.push(image_data);
|
||||
}
|
||||
|
||||
// Calculate the final width and height of the sprite
|
||||
let img_width_out: u32 = images.iter().map(|im| im.width()).sum();
|
||||
let img_height_out: u32 = images.iter().map(|im| im.height()).max().unwrap_or(0);
|
||||
|
||||
// Initialize an image buffer with the appropriate size.
|
||||
let mut imgbuf = image::ImageBuffer::new(img_width_out, img_height_out);
|
||||
let mut accumulated_width = 0;
|
||||
|
||||
// Copy each input image at the correct location in the output image.
|
||||
for img in images {
|
||||
imgbuf.copy_from(&img, accumulated_width, 0).unwrap();
|
||||
accumulated_width += img.width();
|
||||
}
|
||||
|
||||
// Get the output directory
|
||||
let output_dir = sprite_name_to_output_dir(sprite_name);
|
||||
|
||||
// Create the output directory if it doesn't exist
|
||||
if !output_dir.exists() {
|
||||
std::fs::create_dir_all(output_dir.as_path()).unwrap();
|
||||
}
|
||||
|
||||
// Write the output image to disk.
|
||||
imgbuf.save(output_dir.join(format!("{}.png", sprite_name)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
3
automation/anim_stitcher/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
#[cfg(feature = "generator")]
|
||||
pub mod generator;
|
9
game/auto_stitch/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# The Auto-Stitch directory
|
||||
|
||||
This is a bit of a *magic* directory. Anything put in here will automatically be turned into a spritesheet at compile time.
|
||||
|
||||
## File organization
|
||||
|
||||
In this directory, framesets are expected to be stored in subdirectories. The names of these are important as they will translate into the names of the spritesheets.
|
||||
|
||||
For example, if you put a set of frames in `auto_stitch/chr_testFox`, you will get a spritesheet generated to `dist/gen/anm/chr/chr_testFox.png`.
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
1
game/dist/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/gen/*
|
@ -3,20 +3,26 @@ name = "game_logic"
|
||||
publish = false
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[dependencies]
|
||||
raylib = { version = "3.7", path = "../../third_party/raylib-rs/raylib" }
|
||||
sad_machine = { version = "1.0", path = "../../third_party/sm" }
|
||||
tokio = { version = "1.17.0", featurs = ["fs"] }
|
||||
tokio = { version = "1.17.0", features = ["fs"] }
|
||||
log = "0.4.14"
|
||||
profiling = "1.0.5"
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
serde_json = "1.0.79"
|
||||
directories = "4.0.1"
|
||||
chrono = { verison = "0.4.19", features = ["serde"] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
discord-sdk = "0.3.0"
|
||||
rust-embed = { version = "6.2.0", features = ["compression"] }
|
||||
thiserror = "1.0.30"
|
||||
# nalgebra = { version = "0.30.1", features = ["serde"] }
|
||||
approx = "0.5.1"
|
||||
approx = "0.5.1"
|
||||
|
||||
[build-dependencies.anim_stitcher]
|
||||
version = "*"
|
||||
features = ["generator"]
|
||||
path = "../../automation/anim_stitcher"
|
||||
|
26
game/game_logic/build.rs
Normal file
@ -0,0 +1,26 @@
|
||||
//! This script contains code that generates more code.
|
||||
//!
|
||||
//! The idea being that we can have assets auto-pack and optimize themselves at build time.
|
||||
|
||||
fn main() {
|
||||
// We want to re-build if the assets change
|
||||
println!("cargo:rerun-if-changed=../auto_stitch");
|
||||
|
||||
// Search for all direct children of the auto_stitch directory
|
||||
for entry in std::fs::read_dir("../auto_stitch").unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
|
||||
// Get all children of the current entry
|
||||
if let Ok(children) = std::fs::read_dir(&path) {
|
||||
let children_paths = children.map(|e| e.unwrap().path()).collect::<Vec<_>>();
|
||||
|
||||
// Process into a sprite
|
||||
anim_stitcher::generator::stitch_sprites(
|
||||
path.file_name().unwrap().to_str().unwrap(),
|
||||
children_paths,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|