Skip to content

Commit

Permalink
Merge pull request #636 from AmbientRun/cli_asset_import
Browse files Browse the repository at this point in the history
add import for cli assets command
  • Loading branch information
chaosprint authored Aug 4, 2023
2 parents 0475854 + 73daa7f commit 20c7dfa
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ These PRs are not directly user-facing, but improve the development experience.
- **ECS**: `Duration` is now a supported primitive type.
- **ECS**: All integer types from 8-bit to 64-bit are now supported as component types, including signed and unsigned variants. Additionally, all signed and unsigned integer vector types are now supported. This includes `U16`, `IVec2`, `UVec3`, etc.
- **Docs**: The IDE documentation has been improved, including information on how to set up Emacs for Ambient development (thanks to [@kevzettler](https://github.com/kevzettler) in [#505](https://github.com/AmbientRun/Ambient/pull/505)).
- **Assets**: Now you can use `ambient assets import` to import asset one by one. This will create or modify the `pipeline.toml` file for you.

#### Examples

Expand Down
1 change: 0 additions & 1 deletion app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ tracing-stackdriver = { workspace = true, optional = true }
tracing-subscriber = { workspace = true, optional = true }
tracing-log = { workspace = true, optional = true }
time = { workspace = true }

anyhow = { workspace = true }
bincode = { workspace = true }
byteorder = { workspace = true }
Expand Down
26 changes: 24 additions & 2 deletions app/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,37 @@ pub enum Commands {
Assets {
#[command(subcommand)]
command: AssetCommand,
path: PathBuf,
},
}

#[derive(Args, Clone, Debug)]
pub struct MigrateOptions {
#[arg(index = 1, default_value = "./assets")]
/// The path to the assets folder
pub path: PathBuf,
}

#[derive(Args, Clone, Debug)]
pub struct ImportOptions {
#[arg(index = 1)]
/// The path to the assets you want to import
pub path: PathBuf,
#[arg(long)]
/// Whether to convert audio files to OGG
pub convert_audio: bool,
/// Whether to generate a collider from the model
#[arg(long)]
pub collider_from_model: bool,
}

#[derive(Subcommand, Clone, Debug)]
pub enum AssetCommand {
/// Migrate json pipelines to toml
#[command(name = "migrate-pipelines-toml")]
MigratePipelinesToml,
MigratePipelinesToml(MigrateOptions),
/// Import new assets with interactive prompts
#[command(name = "import")]
Import(ImportOptions),
}

#[derive(Subcommand, Clone, Copy, Debug)]
Expand Down
31 changes: 26 additions & 5 deletions app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,37 @@ async fn main() -> anyhow::Result<()> {
return Ok(());
}

if let Commands::Assets { command, path } = &cli.command {
let path = ProjectPath::new_local(path.clone())?;
let manifest = load_manifest(&assets, &path).await?;

if let Commands::Assets { command } = &cli.command {
match command {
AssetCommand::MigratePipelinesToml => {
AssetCommand::MigratePipelinesToml(opt) => {
let path = ProjectPath::new_local(opt.path.clone())?;
let manifest = load_manifest(&assets, &path).await?;
ambient_build::migrate::toml::process(&manifest, path.fs_path.unwrap())
.await
.context("Failed to migrate pipelines")?;
}
AssetCommand::Import(opt) => match opt.path.extension() {
Some(ext) => {
if ext == "wav" || ext == "mp3" || ext == "ogg" {
let convert = opt.convert_audio;
ambient_build::pipelines::import_audio(opt.path.clone(), convert)
.context("failed to import audio")?;
} else if ext == "fbx" || ext == "glb" || ext == "gltf" || ext == "obj" {
let collider_from_model = opt.collider_from_model;
ambient_build::pipelines::import_model(
opt.path.clone(),
collider_from_model,
)
.context("failed to import models")?;
} else if ext == "jpg" || ext == "png" || ext == "gif" || ext == "webp" {
// TODO: import textures API may change, so this is just a placeholder
todo!();
} else {
bail!("Unsupported file type");
}
}
None => bail!("Unknown file type"),
},
}

return Ok(());
Expand Down
1 change: 0 additions & 1 deletion crates/build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ ambient_project = { path = "../../shared_crates/project" }
ambient_std = { path = "../../shared_crates/std" }

ambient_unity_parser = { path = "../../libs/unity_parser" }

walkdir = { workspace = true }
futures = { workspace = true }
tokio = { workspace = true }
Expand Down
138 changes: 138 additions & 0 deletions crates/build/src/pipelines/importer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use anyhow::Context;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use toml::map::Map;
use toml::Value;

pub fn import_audio(path: PathBuf, convert: bool) -> anyhow::Result<()> {
let current_dir = std::env::current_dir().context("Error getting current directory")?;
let asset_folder_path = current_dir.join("assets");
let tomlpath = current_dir.join("assets/pipeline.toml");
println!("Importing audio...");
println!("Read more about audio usage here: https://ambientrun.github.io/Ambient/reference/audio.html");
if !Path::new(&asset_folder_path).exists() {
std::fs::create_dir_all(&asset_folder_path)?;
}
if !Path::new(&tomlpath).exists() {
File::create(&tomlpath)?;
}

let mut file = File::open(&tomlpath)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;

let mut data: Value = match toml::from_str(&contents) {
Ok(v) => v,
Err(_) => Value::Table(Map::new()), // if we cannot parse the file, start with a fresh table
};

if let Value::Table(table) = &mut data {
let pipelines = match table.get_mut("pipelines") {
Some(Value::Array(arr)) => arr,
_ => {
table.insert("pipelines".to_string(), Value::Array(Vec::new()));
match table.get_mut("pipelines") {
Some(Value::Array(arr)) => arr,
_ => panic!("Unexpected state"),
}
}
};

let mut new_pipeline = Map::new();
new_pipeline.insert(String::from("type"), Value::String(String::from("Audio")));
new_pipeline.insert(String::from("convert"), Value::Boolean(convert));
let filename_with_ext = path.file_name().unwrap().to_str().unwrap().to_string();
new_pipeline.insert(
String::from("sources"),
Value::Array(vec![Value::String(filename_with_ext)]),
);

pipelines.push(Value::Table(new_pipeline));
} else {
panic!("Expected table at the root of the TOML document");
}

let mut file = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(tomlpath)?;
write!(file, "{}", toml::to_string(&data)?)?;

let file_name = path.file_name().unwrap(); // get the file name from the path
let destination = asset_folder_path.join(file_name);
std::fs::copy(path.clone(), destination).context("Error copying audio file")?;
Ok(())
}

pub fn import_model(path: PathBuf, collider_from_model: bool) -> anyhow::Result<()> {
let current_dir = std::env::current_dir().context("Error getting current directory")?;
let asset_folder_path = current_dir.join("assets");
let tomlpath = current_dir.join("assets/pipeline.toml");
println!("Importing model...");
println!("Read more about model import here: https://ambientrun.github.io/Ambient/reference/asset_pipeline.html");
if !Path::new(&asset_folder_path).exists() {
std::fs::create_dir_all(&asset_folder_path)?;
}
if !Path::new(&tomlpath).exists() {
File::create(&tomlpath)?;
}

let mut file = File::open(&tomlpath)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;

let mut data: Value = match toml::from_str(&contents) {
Ok(v) => v,
Err(_) => Value::Table(Map::new()), // if we cannot parse the file, start with a fresh table
};

if let Value::Table(table) = &mut data {
if collider_from_model {
let mut collider_pipeline = Map::new();
collider_pipeline.insert(
String::from("type"),
Value::String(String::from("FromModel")),
);

table.insert(
"pipelines.collider".to_string(),
Value::Table(collider_pipeline),
);
}
let pipelines = match table.get_mut("pipelines") {
Some(Value::Array(a)) => a,
_ => {
table.insert("pipelines".to_string(), Value::Array(Vec::new()));
match table.get_mut("pipelines") {
Some(Value::Array(a)) => a,
_ => panic!("Unexpected state"),
}
}
};

let mut new_pipeline = Map::new();
new_pipeline.insert(String::from("type"), Value::String(String::from("Models")));

let filename_with_ext = path.file_name().unwrap().to_str().unwrap().to_string();
new_pipeline.insert(
String::from("sources"),
Value::Array(vec![Value::String(filename_with_ext)]),
);

pipelines.push(Value::Table(new_pipeline));
}

let mut file = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(tomlpath)?;
let toml_string = toml::to_string(&data)?;
let s = toml_string.replace(r#""pipelines.collider""#, r#"pipelines.collider"#);
write!(file, "{}", s)?;

let file_name = path.file_name().unwrap(); // get the file name from the path
let destination = asset_folder_path.join(file_name);
std::fs::copy(path.clone(), destination).context("Error copying audio file")?;
Ok(())
}
3 changes: 3 additions & 0 deletions crates/build/src/pipelines/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ use out_asset::{OutAsset, OutAssetContent, OutAssetPreview};

pub mod audio;
pub mod context;
pub mod importer;
pub mod materials;
pub mod models;
pub mod out_asset;

pub use importer::*;

pub async fn process_pipeline(pipeline: &Pipeline, ctx: PipelineCtx) -> Vec<OutAsset> {
log::info!("Processing pipeline: {:?}", ctx.pipeline_path());
let mut assets = match &pipeline.processor {
Expand Down
7 changes: 7 additions & 0 deletions guest/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 20c7dfa

Please sign in to comment.