Enhance CLI functionality with palette support and update exporters to use BlockPalette
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
*.mcfunction
|
||||||
27
Cargo.lock
generated
27
Cargo.lock
generated
@@ -209,6 +209,12 @@ version = "1.70.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -255,6 +261,8 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"exporters",
|
"exporters",
|
||||||
"lib",
|
"lib",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@@ -381,6 +389,19 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.149"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -547,3 +568,9 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ lib = { path = "../lib" }
|
|||||||
exporters = { path = "../exporters" }
|
exporters = { path = "../exporters" }
|
||||||
clap = { version = "4.6.0", features = ["derive"] }
|
clap = { version = "4.6.0", features = ["derive"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = "1"
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||||
|
|||||||
@@ -2,26 +2,34 @@ use std::{fs, path::PathBuf};
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use exporters::McFunctionExporter;
|
use exporters::McFunctionExporter;
|
||||||
use lib::{StructureExporter, TextBuilder, TtfFont};
|
use lib::{BlockPalette, StructureExporter, TextBuilder, TtfFont};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// The text you want to generate
|
/// The text you want to generate
|
||||||
#[arg(short, long)]
|
#[arg(short, long, required_unless_present = "list_palettes")]
|
||||||
text: String,
|
text: Option<String>,
|
||||||
|
|
||||||
/// Path to the .ttf font file
|
/// Path to the .ttf font file
|
||||||
#[arg(short, long)]
|
#[arg(short, long, required_unless_present = "list_palettes")]
|
||||||
font: PathBuf,
|
font: Option<PathBuf>,
|
||||||
|
|
||||||
/// How many blocks deep the text should be
|
/// How many blocks deep the text should be
|
||||||
#[arg(short, long, default_value_t = 1)]
|
#[arg(short, long, default_value_t = 1)]
|
||||||
depth: u32,
|
depth: u32,
|
||||||
|
|
||||||
/// The Minecraft block ID to use for the text body
|
/// Name of a built-in palette preset (from the palettes/ directory)
|
||||||
#[arg(short, long, default_value = "minecraft:quartz_block")]
|
#[arg(short, long, conflicts_with = "palette_file")]
|
||||||
block: String,
|
palette: Option<String>,
|
||||||
|
|
||||||
|
/// Path to a JSON palette file
|
||||||
|
#[arg(long)]
|
||||||
|
palette_file: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// List available palette presets and exit
|
||||||
|
#[arg(long)]
|
||||||
|
list_palettes: bool,
|
||||||
|
|
||||||
/// Output file path (without extension)
|
/// Output file path (without extension)
|
||||||
#[arg(short, long, default_value = "output")]
|
#[arg(short, long, default_value = "output")]
|
||||||
@@ -32,26 +40,70 @@ struct Cli {
|
|||||||
size: f32,
|
size: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn palettes_dir() -> PathBuf {
|
||||||
|
std::env::current_exe()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("palettes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_palette_by_name(name: &str) -> anyhow::Result<BlockPalette> {
|
||||||
|
let path = palettes_dir().join(format!("{}.json", name));
|
||||||
|
let json = fs::read_to_string(&path)
|
||||||
|
.map_err(|_| anyhow::anyhow!("palette '{}' not found in {:?}", name, palettes_dir()))?;
|
||||||
|
Ok(serde_json::from_str(&json)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run() -> anyhow::Result<()> {
|
pub fn run() -> anyhow::Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
tracing::info!("loading font from: {:?}", cli.font);
|
if cli.list_palettes {
|
||||||
let font_bytes = fs::read(&cli.font)?;
|
let dir = palettes_dir();
|
||||||
|
let entries = fs::read_dir(&dir)
|
||||||
|
.map_err(|_| anyhow::anyhow!("palettes directory not found at {:?}", dir))?;
|
||||||
|
println!("Available palettes:");
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
let path = entry.path();
|
||||||
|
if path.extension().and_then(|e| e.to_str()) == Some("json") {
|
||||||
|
if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
|
||||||
|
println!(" {}", stem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let font = TtfFont::from_bytes(&font_bytes, cli.size)
|
let text = cli.text.unwrap();
|
||||||
.map_err(|e| anyhow::anyhow!(e))?;
|
let font_path = cli.font.unwrap();
|
||||||
|
|
||||||
tracing::info!("generating voxel grid for text: '{}'", cli.text);
|
let palette = if let Some(path) = cli.palette_file {
|
||||||
|
let json = fs::read_to_string(&path)
|
||||||
|
.map_err(|e| anyhow::anyhow!("failed to read palette file {:?}: {}", path, e))?;
|
||||||
|
serde_json::from_str(&json)?
|
||||||
|
} else {
|
||||||
|
let name = cli.palette.as_deref().unwrap_or("default");
|
||||||
|
load_palette_by_name(name)?
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::info!("using palette: {}", palette.name);
|
||||||
|
tracing::info!("loading font from: {:?}", font_path);
|
||||||
|
let font_bytes = fs::read(&font_path)?;
|
||||||
|
|
||||||
|
let font = TtfFont::from_bytes(&font_bytes, cli.size).map_err(|e| anyhow::anyhow!(e))?;
|
||||||
|
|
||||||
|
tracing::info!("generating voxel grid for text: '{}'", text);
|
||||||
let builder = TextBuilder::new(&font).with_depth(cli.depth);
|
let builder = TextBuilder::new(&font).with_depth(cli.depth);
|
||||||
let grid = builder.generate(&cli.text);
|
let grid = builder.generate(&text);
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"grid generated: {}x{}x{}",
|
"grid generated: {}x{}x{}",
|
||||||
grid.width, grid.height, grid.depth
|
grid.width,
|
||||||
|
grid.height,
|
||||||
|
grid.depth
|
||||||
);
|
);
|
||||||
|
|
||||||
let exporter = McFunctionExporter::new(&cli.block, "minecraft:obsidian");
|
let exporter = McFunctionExporter::new(&palette);
|
||||||
|
|
||||||
let output_bytes = exporter.export(&grid)?;
|
let output_bytes = exporter.export(&grid)?;
|
||||||
|
|
||||||
let mut out_path = cli.out.clone();
|
let mut out_path = cli.out.clone();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
mod litematica;
|
mod litematica;
|
||||||
mod mcfunction;
|
mod mcfunction;
|
||||||
|
|
||||||
|
pub use litematica::LitematicaExporter;
|
||||||
pub use mcfunction::McFunctionExporter;
|
pub use mcfunction::McFunctionExporter;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::{
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use lib::{StructureExporter, VoxelType};
|
use lib::{BlockPalette, StructureExporter, VoxelType};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -61,10 +61,11 @@ pub struct LitematicaExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LitematicaExporter {
|
impl LitematicaExporter {
|
||||||
pub fn new(body_block: &str, outline_block: &str) -> Self {
|
pub fn new(palette: &BlockPalette) -> Self {
|
||||||
let mut palette_map = HashMap::new();
|
let mut palette_map = HashMap::new();
|
||||||
palette_map.insert(VoxelType::Body, body_block.to_string());
|
palette_map.insert(VoxelType::Body, palette.resolve(&VoxelType::Body));
|
||||||
palette_map.insert(VoxelType::Outline, outline_block.to_string());
|
palette_map.insert(VoxelType::Outline, palette.resolve(&VoxelType::Outline));
|
||||||
|
palette_map.insert(VoxelType::Shadow, palette.resolve(&VoxelType::Shadow));
|
||||||
Self { palette_map }
|
Self { palette_map }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use lib::{StructureExporter, VoxelType};
|
use lib::{BlockPalette, StructureExporter, VoxelType};
|
||||||
|
|
||||||
pub struct McFunctionExporter {
|
pub struct McFunctionExporter {
|
||||||
palette: HashMap<VoxelType, String>,
|
palette: HashMap<VoxelType, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl McFunctionExporter {
|
impl McFunctionExporter {
|
||||||
pub fn new(body_block: &str, outline_block: &str) -> Self {
|
pub fn new(palette: &BlockPalette) -> Self {
|
||||||
let mut palette = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
map.insert(VoxelType::Body, palette.resolve(&VoxelType::Body));
|
||||||
palette.insert(VoxelType::Body, body_block.to_string());
|
map.insert(VoxelType::Outline, palette.resolve(&VoxelType::Outline));
|
||||||
palette.insert(VoxelType::Outline, outline_block.to_string());
|
map.insert(VoxelType::Shadow, palette.resolve(&VoxelType::Shadow));
|
||||||
|
Self { palette: map }
|
||||||
Self { palette }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub use font::FontProvider;
|
|||||||
pub use fonts::ttf_font::TtfFont;
|
pub use fonts::ttf_font::TtfFont;
|
||||||
pub use grid::VoxelGrid;
|
pub use grid::VoxelGrid;
|
||||||
pub use models::VoxelType;
|
pub use models::VoxelType;
|
||||||
|
pub use palette::BlockPalette;
|
||||||
|
|
||||||
pub trait StructureExporter {
|
pub trait StructureExporter {
|
||||||
fn export(&self, grid: &VoxelGrid) -> anyhow::Result<Vec<u8>>;
|
fn export(&self, grid: &VoxelGrid) -> anyhow::Result<Vec<u8>>;
|
||||||
|
|||||||
9
palettes/default.json
Normal file
9
palettes/default.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "Default",
|
||||||
|
"author": null,
|
||||||
|
"blocks": {
|
||||||
|
"body": "minecraft:quartz_block",
|
||||||
|
"outline": "minecraft:obsidian",
|
||||||
|
"shadow": null
|
||||||
|
}
|
||||||
|
}
|
||||||
9
palettes/gold.json
Normal file
9
palettes/gold.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "Gold",
|
||||||
|
"author": null,
|
||||||
|
"blocks": {
|
||||||
|
"body": "minecraft:gold_block",
|
||||||
|
"outline": "minecraft:deepslate",
|
||||||
|
"shadow": null
|
||||||
|
}
|
||||||
|
}
|
||||||
9
palettes/ocean.json
Normal file
9
palettes/ocean.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "Ocean",
|
||||||
|
"author": null,
|
||||||
|
"blocks": {
|
||||||
|
"body": "minecraft:blue_concrete",
|
||||||
|
"outline": "minecraft:white_concrete",
|
||||||
|
"shadow": "minecraft:gray_concrete"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
palettes/stone.json
Normal file
9
palettes/stone.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "Stone",
|
||||||
|
"author": null,
|
||||||
|
"blocks": {
|
||||||
|
"body": "minecraft:stone",
|
||||||
|
"outline": "minecraft:cobblestone",
|
||||||
|
"shadow": null
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user