Find texture path
now missing part is reading gltf file, changing materials texture and saving it
This commit is contained in:
parent
6e75fff4e4
commit
8ef65376b8
|
|
@ -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"
|
||||||
|
|
|
||||||
68
src/asset.rs
68
src/asset.rs
|
|
@ -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()))
|
||||||
|
}
|
||||||
|
|
|
||||||
103
src/unpacker.rs
103
src/unpacker.rs
|
|
@ -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!(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue