Find texture path

now missing part is reading gltf file, changing materials texture and saving it
This commit is contained in:
Piotr Siuszko 2023-12-28 20:41:49 +01:00
parent 6e75fff4e4
commit 8ef65376b8
3 changed files with 125 additions and 51 deletions

View File

@ -24,4 +24,5 @@ clap = { version = "4.4", features = ["derive"] }
flate2 = "1.0" flate2 = "1.0"
gltf = "1.4.0" gltf = "1.4.0"
rayon = "1.8.0" rayon = "1.8.0"
regex = "1.10.2"
tar = "0.4" tar = "0.4"

View File

@ -1,3 +1,4 @@
use regex::Regex;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;
use std::fs::DirEntry; use std::fs::DirEntry;
@ -5,14 +6,15 @@ use std::fs::File;
use std::io::BufRead; use std::io::BufRead;
use std::io::BufReader; use std::io::BufReader;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
#[derive(Clone)] #[derive(Clone)]
pub struct Asset { pub struct Asset {
pub extension: Option<String>, pub extension: Option<String>,
pub hash: String, pub guid: String,
pub path_name: String, pub path: String,
pub has_meta: bool, pub has_meta: bool,
pub asset_type: AssetType pub asset_type: AssetType,
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
@ -21,16 +23,54 @@ pub enum AssetType{
Material, Material,
Prefab, Prefab,
Scene, Scene,
Other(String) Other(String),
} }
impl Asset { impl Asset {
pub fn from_path(entry: &DirEntry) -> Option<Asset> { pub fn try_get_mat_texture_guid(&self) -> Option<String> {
match &self.asset_type {
AssetType::Material => {}
_ => return None,
}
let file = File::open(&self.path).unwrap();
let buf_reader = BufReader::new(file);
let search = buf_reader.lines().into_iter().find(|s| {
let ss = s.as_ref().unwrap();
return ss.contains("m_Texture") && ss.contains("guid: ");
});
if let Some(line) = search {
let line = line.unwrap_or_default();
return extract_guid(&line);
}
None
}
pub fn prepare_directory(&self) {
println!("{}: {:?}", self.guid, self.path);
let base_path = Path::new(&self.path);
let result_dir = base_path.parent();
if result_dir.is_none() {
eprintln!("{} is none", &self.path);
}
let result_dir = result_dir.unwrap();
if !result_dir.exists() {
let result = fs::create_dir_all(result_dir);
if result.is_err() {
eprintln!(
"Error {}: {}",
result_dir.to_str().unwrap(),
result.err().unwrap()
);
}
}
}
pub fn from_path(entry: &DirEntry, output_dir: &PathBuf) -> Option<Asset> {
let root_file = entry.path(); let root_file = entry.path();
if !root_file.is_dir() { if !root_file.is_dir() {
return None; return None;
} }
let asset = entry.file_name().into_string().unwrap(); let guid = entry.file_name().into_string().unwrap();
let mut real_path = String::new(); let mut real_path = String::new();
let mut extension = None; let mut extension = None;
let mut has_asset = false; let mut has_asset = false;
@ -46,7 +86,7 @@ impl Asset {
let line = buf_reader.lines().next(); let line = buf_reader.lines().next();
match line { match line {
Some(Ok(path)) => { Some(Ok(path)) => {
real_path = path; real_path = output_dir.join(path).to_str().unwrap().to_string();
if let Some(e) = if let Some(e) =
Path::new(&real_path).extension().and_then(OsStr::to_str) Path::new(&real_path).extension().and_then(OsStr::to_str)
{ {
@ -68,14 +108,14 @@ impl Asset {
"prefab" => AssetType::Prefab, "prefab" => AssetType::Prefab,
"unity" => AssetType::Scene, "unity" => AssetType::Scene,
"mat" => AssetType::Material, "mat" => AssetType::Material,
_ => AssetType::Other(str.clone()) _ => AssetType::Other(str.clone()),
}, },
_ => AssetType::Other(String::new()) _ => AssetType::Other(String::new()),
}; };
Some(Asset { Some(Asset {
extension, extension,
hash: asset, guid,
path_name: real_path, path: real_path,
has_meta, has_meta,
asset_type, asset_type,
}) })
@ -84,3 +124,9 @@ impl Asset {
} }
} }
} }
fn extract_guid(text: &str) -> Option<String> {
let re = Regex::new(r"guid: (?P<guid>[A-Za-z0-9]{32})").unwrap();
re.captures(text)
.and_then(|cap| cap.name("guid").map(|guid| guid.as_str().to_string()))
}

View File

@ -7,7 +7,6 @@ use std::process::Command;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::sync::Arc; use std::sync::Arc;
use std::{fs, io}; use std::{fs, io};
use std::hash::Hash;
use tar::Archive; use tar::Archive;
#[derive(Clone)] #[derive(Clone)]
@ -36,6 +35,7 @@ impl Unpacker {
pub fn extract(&mut self) { pub fn extract(&mut self) {
let archive_path = Path::new(&self.args.input); let archive_path = Path::new(&self.args.input);
let tmp_path = Path::new("./tmp_dir"); let tmp_path = Path::new("./tmp_dir");
let output_dir = Path::new(&self.args.output);
if let Err(e) = Unpacker::extract_archive(archive_path, tmp_path) { if let Err(e) = Unpacker::extract_archive(archive_path, tmp_path) {
println!("Failed to extract archive: {}", e); println!("Failed to extract archive: {}", e);
@ -49,7 +49,7 @@ impl Unpacker {
.par_bridge() .par_bridge()
.for_each_with(sender, |s, entry| { .for_each_with(sender, |s, entry| {
let entry = entry.unwrap(); let entry = entry.unwrap();
let asset = crate::asset::Asset::from_path(&entry); let asset = crate::asset::Asset::from_path(&entry, &output_dir.to_path_buf());
if let Some(asset) = asset { if let Some(asset) = asset {
let extension = &asset.extension.clone().unwrap_or_default(); let extension = &asset.extension.clone().unwrap_or_default();
if !ignored_extensions.contains(extension) { if !ignored_extensions.contains(extension) {
@ -64,29 +64,67 @@ impl Unpacker {
if self.args.fbx_to_gltf.is_none() { if self.args.fbx_to_gltf.is_none() {
return; return;
} }
let output_dir = Path::new(&self.args.output); let fbx_models: Vec<Asset> = self
.assets
let fbx_models : Vec<Asset> = self.assets.clone().into_iter().filter(|a| &a.asset_type == &AssetType::FbxModel).collect(); .clone()
let prefabs : Vec<Asset> = self.assets.clone().into_iter().filter(|a| &a.asset_type == &AssetType::Prefab).collect(); .into_iter()
let materials : Vec<Asset> = self.assets.clone().into_iter().filter(|a| &a.asset_type == &AssetType::Material).collect(); .filter(|a| &a.asset_type == &AssetType::FbxModel)
println!("There are {} models, {} prefabs and {} materials", fbx_models.len(), prefabs.len(), materials.len()); .collect();
let prefabs: Vec<Asset> = self
.assets
.clone()
.into_iter()
.filter(|a| &a.asset_type == &AssetType::Prefab)
.collect();
let materials: Vec<Asset> = self
.assets
.clone()
.into_iter()
.filter(|a| &a.asset_type == &AssetType::Material)
.collect();
println!(
"There are {} models, {} prefabs and {} materials",
fbx_models.len(),
prefabs.len(),
materials.len()
);
let mut counter = 0;
let mut result_models : Vec<Asset> = vec![];
for prefab in prefabs.iter() { for prefab in prefabs.iter() {
let path = Path::new(&prefab.path_name); let path = Path::new(&prefab.path);
let result_path = output_dir.join(path); let prefab_content = fs::read_to_string(&path).unwrap();
let prefab_content = fs::read_to_string(&result_path).unwrap(); let matching_materials: Vec<Asset> = materials
let matching_materials : Vec<Asset> = materials.clone().into_iter().filter(|a| prefab_content.contains(&a.hash)).collect(); .clone()
let matching_models : Vec<Asset> = fbx_models.clone().into_iter().filter(|a| prefab_content.contains(&a.hash)).collect(); .into_iter()
println!("Prefab: {},\nMaterials: ",&prefab.path_name); .filter(|a| prefab_content.contains(&a.guid))
for m in matching_materials.iter() { .collect();
println!(" - {}",&m.path_name); let matching_models: Vec<Asset> = fbx_models
.clone()
.into_iter()
.filter(|a| prefab_content.contains(&a.guid))
.collect();
if matching_materials.len() != 1 || 1 != matching_models.len() {
continue;
} }
println!("Models: "); let material = matching_materials.first().unwrap();
for m in matching_models.iter() { let model = matching_models.first().unwrap();
println!(" - {}",&m.path_name); if result_models.iter().any(|a| model.guid.eq(&a.guid)) {
continue;
} }
let texture_guid: Option<String> = material.try_get_mat_texture_guid();
let texture_asset: &Asset;
match &texture_guid {
Some(guid) => {
texture_asset = self.assets.iter().find(|a| guid.eq(&a.guid)).unwrap()
}
None => continue,
}
// here we should read gltf file and replace material texture with Uri based on texture_asset
// now if there is one material and one model we should read material texture path and assign it to model material texture result_models.push(model.clone());
counter += 1;
} }
println!("Updated {} models", counter);
} }
pub fn process_data(&self) { pub fn process_data(&self) {
@ -97,21 +135,18 @@ impl Unpacker {
let mapping_arc = Arc::new(&self.assets); let mapping_arc = Arc::new(&self.assets);
let tmp_dir = Arc::new(tmp_path); let tmp_dir = Arc::new(tmp_path);
fs::create_dir(output_dir).unwrap(); fs::create_dir(output_dir).unwrap();
let output_dir = Arc::new(output_dir);
mapping_arc.par_iter().for_each(|asset| { mapping_arc.par_iter().for_each(|asset| {
let asset_hash = &asset.hash; let asset_hash = &asset.guid;
let path = Path::new(&asset.path_name); let path = Path::new(&asset.path);
let source_asset = Path::new(&*tmp_dir).join(asset_hash).join("asset"); let source_asset = Path::new(&*tmp_dir).join(asset_hash).join("asset");
let result_path = output_dir.join(path);
process_directory(asset_hash, &asset.path_name, &result_path); asset.prepare_directory();
if copy_meta_files && asset.has_meta { if copy_meta_files && asset.has_meta {
let source_meta = Path::new(&*tmp_dir).join(asset_hash).join("asset.meta"); let source_meta = Path::new(&*tmp_dir).join(asset_hash).join("asset.meta");
let mut meta_path = asset.path_name.clone(); let mut meta_path = asset.path.clone();
meta_path.push_str(".meta"); meta_path.push_str(".meta");
let result_path = output_dir.join(meta_path); fs::rename(source_meta, meta_path).unwrap();
fs::rename(source_meta, result_path).unwrap();
} }
if !source_asset.exists() { if !source_asset.exists() {
@ -121,24 +156,16 @@ impl Unpacker {
if self.args.fbx_to_gltf.is_some() && &asset.asset_type == &AssetType::FbxModel { if self.args.fbx_to_gltf.is_some() && &asset.asset_type == &AssetType::FbxModel {
process_fbx_file( process_fbx_file(
&source_asset, &source_asset,
&result_path, &path,
&self.args.fbx_to_gltf.clone().unwrap(), &self.args.fbx_to_gltf.clone().unwrap(),
); );
} else { } else {
process_non_fbx_file(&source_asset, &result_path); process_non_fbx_file(&source_asset, &path);
} }
}); });
fs::remove_dir_all(Path::new(&*tmp_dir)).unwrap(); fs::remove_dir_all(Path::new(&*tmp_dir)).unwrap();
fn process_directory(asset_hash: &str, asset_path: &str, result_path: &Path) {
println!("{}: {:?}", asset_hash, asset_path);
let result_dir = result_path.parent().unwrap();
if !result_dir.exists() {
fs::create_dir_all(result_dir).unwrap();
}
}
fn process_fbx_file(source_asset: &Path, result_path: &Path, tool: &PathBuf) { fn process_fbx_file(source_asset: &Path, result_path: &Path, tool: &PathBuf) {
let out_path = result_path.with_extension(""); let out_path = result_path.with_extension("");
println!( println!(