added support for embedded textures in fbx

- fixes #136
This commit is contained in:
Dmitry Stepanov
2024-02-17 14:36:21 +03:00
parent bcd98eafb9
commit 512f32b8b0
7 changed files with 108 additions and 32 deletions
+1
View File
@@ -65,6 +65,7 @@ clap = { version = "4", features = ["derive"] }
winit = { version = "0.29.2", features = ["serde"] }
half = "2.2.1"
fast_image_resize = "2.7.0"
base64 = "0.21.0"
[features]
enable_profiler = ["fyrox-core/enable_profiler"]
+7
View File
@@ -7,6 +7,7 @@ pub enum FbxAttribute {
Long(i64),
Bool(bool),
String(String), // ASCII Fbx always have every attribute in string form
RawData(Vec<u8>),
}
impl std::fmt::Display for FbxAttribute {
@@ -18,6 +19,7 @@ impl std::fmt::Display for FbxAttribute {
FbxAttribute::Long(long) => write!(f, "{}", long),
FbxAttribute::Bool(boolean) => write!(f, "{}", boolean),
FbxAttribute::String(string) => write!(f, "{}", string),
FbxAttribute::RawData(raw) => write!(f, "{:?}", raw),
}
}
}
@@ -34,6 +36,7 @@ impl FbxAttribute {
Ok(i) => Ok(i),
Err(_) => Err(format!("Unable to convert string {} to i32", val)),
},
FbxAttribute::RawData(_) => Err("Unable to convert raw data to i32".to_string()),
}
}
@@ -48,6 +51,7 @@ impl FbxAttribute {
Ok(i) => Ok(i),
Err(_) => Err(format!("Unable to convert string {} to i64", val)),
},
FbxAttribute::RawData(_) => Err("Unable to convert raw data to i64".to_string()),
}
}
@@ -62,6 +66,7 @@ impl FbxAttribute {
Ok(i) => Ok(i),
Err(_) => Err(format!("Unable to convert string {} to f64", val)),
},
FbxAttribute::RawData(_) => Err("Unable to convert raw data to f64".to_string()),
}
}
@@ -76,6 +81,7 @@ impl FbxAttribute {
Ok(i) => Ok(i),
Err(_) => Err(format!("Unable to convert string {} to f32", val)),
},
FbxAttribute::RawData(_) => Err("Unable to convert raw data to f32".to_string()),
}
}
@@ -87,6 +93,7 @@ impl FbxAttribute {
FbxAttribute::Long(val) => val.to_string(),
FbxAttribute::Bool(val) => val.to_string(),
FbxAttribute::String(val) => val.clone(),
FbxAttribute::RawData(val) => String::from_utf8_lossy(val).to_string(),
}
}
}
+5 -2
View File
@@ -143,9 +143,12 @@ where
.attributes
.push(read_string(file)?),
b'R' => {
// Ignore Raw data
let length = i64::from(file.read_u32::<LittleEndian>()?);
file.seek(SeekFrom::Current(length))?;
let mut data = vec![0; length as usize];
file.read_exact(&mut data)?;
pool.borrow_mut(node_handle)
.attributes
.push(FbxAttribute::RawData(data));
}
_ => (),
}
+45 -30
View File
@@ -10,6 +10,7 @@ mod document;
pub mod error;
mod scene;
use crate::resource::texture::{TextureImportOptions, TextureResource, TextureResourceExtension};
use crate::{
asset::manager::ResourceManager,
core::{
@@ -59,6 +60,7 @@ use crate::{
};
use fxhash::{FxHashMap, FxHashSet};
use fyrox_resource::io::ResourceIo;
use fyrox_resource::untyped::ResourceKind;
use std::{cmp::Ordering, path::Path};
/// Input angles in degrees
@@ -308,47 +310,60 @@ async fn create_surfaces(
let path = texture.get_file_path();
if let Some(filename) = path.file_name() {
let texture_path = match model_import_options.material_search_options {
MaterialSearchOptions::MaterialsDirectory(ref directory) => {
Some(directory.join(filename))
}
MaterialSearchOptions::RecursiveUp => {
let mut texture_path = None;
let mut path = model_path.to_owned();
while let Some(parent) = path.parent() {
let candidate = parent.join(filename);
if io.exists(&candidate).await {
texture_path = Some(candidate);
break;
}
path.pop();
let texture_path = if texture.content.is_empty() {
match model_import_options.material_search_options {
MaterialSearchOptions::MaterialsDirectory(ref directory) => {
Some(directory.join(filename))
}
texture_path
}
MaterialSearchOptions::WorkingDirectory => {
let mut texture_path = None;
MaterialSearchOptions::RecursiveUp => {
let mut texture_path = None;
let mut path = model_path.to_owned();
while let Some(parent) = path.parent() {
let candidate = parent.join(filename);
if io.exists(&candidate).await {
texture_path = Some(candidate);
break;
}
path.pop();
}
texture_path
}
MaterialSearchOptions::WorkingDirectory => {
let mut texture_path = None;
let path = Path::new(".");
let path = Path::new(".");
if let Ok(iter) = io.walk_directory(path).await {
for dir in iter {
if io.is_dir(&dir).await {
let candidate = dir.join(filename);
if candidate.exists() {
texture_path = Some(candidate);
break;
if let Ok(iter) = io.walk_directory(path).await {
for dir in iter {
if io.is_dir(&dir).await {
let candidate = dir.join(filename);
if candidate.exists() {
texture_path = Some(candidate);
break;
}
}
}
}
}
texture_path
texture_path
}
MaterialSearchOptions::UsePathDirectly => Some(path.clone()),
}
MaterialSearchOptions::UsePathDirectly => Some(path.clone()),
} else {
Some(path.clone())
};
if let Some(texture_path) = texture_path {
let texture = resource_manager.request::<Texture>(texture_path.as_path());
let texture = if texture.content.is_empty() {
resource_manager.request::<Texture>(texture_path.as_path())
} else {
TextureResource::load_from_memory(
ResourceKind::External(texture_path.clone()),
&texture.content,
TextureImportOptions::default(),
)
.unwrap()
};
// Make up your mind, Autodesk and Blender.
// Handle all possible combinations of links to auto-import materials.
+14
View File
@@ -1,3 +1,4 @@
use crate::resource::fbx::scene::video::FbxVideo;
use crate::{
core::{
algebra::{Matrix4, Vector3},
@@ -24,6 +25,7 @@ pub mod geometry;
pub mod light;
pub mod model;
pub mod texture;
pub mod video;
pub struct FbxScene {
components: Pool<FbxComponent>,
@@ -83,6 +85,12 @@ impl FbxScene {
nodes,
)?));
}
"Video" => {
if object.get_attrib(2)?.as_string() == "Clip" {
component_handle = components
.spawn(FbxComponent::Video(FbxVideo::read(*object_handle, nodes)?));
}
}
"NodeAttribute" => {
if object.attrib_count() > 2 && object.get_attrib(2)?.as_string() == "Light" {
component_handle = components
@@ -207,6 +215,11 @@ fn link_child_with_parent_component(
channel.geometry = child_handle;
}
}
FbxComponent::Texture(texture) => {
if let FbxComponent::Video(video) = child {
texture.content = video.content.clone();
}
}
// Ignore rest
_ => (),
}
@@ -217,6 +230,7 @@ pub enum FbxComponent {
Cluster(FbxCluster),
BlendShapeChannel(FbxBlendShapeChannel),
Texture(FbxTexture),
Video(FbxVideo),
Light(FbxLight),
Model(Box<FbxModel>),
Material(FbxMaterial),
+2
View File
@@ -6,6 +6,7 @@ use std::path::PathBuf;
pub struct FbxTexture {
filename: PathBuf,
pub content: Vec<u8>,
}
impl FbxTexture {
@@ -15,6 +16,7 @@ impl FbxTexture {
) -> Result<Self, String> {
let mut texture = FbxTexture {
filename: PathBuf::new(),
content: Default::default(),
};
if let Ok(relative_file_name_node) =
nodes.get_by_name(texture_node_handle, "RelativeFilename")
+34
View File
@@ -0,0 +1,34 @@
use crate::{
core::pool::Handle,
resource::fbx::document::{attribute::FbxAttribute, FbxNode, FbxNodeContainer},
};
use base64::Engine;
pub struct FbxVideo {
pub content: Vec<u8>,
}
impl FbxVideo {
pub(in crate::resource::fbx) fn read(
video_node_handle: Handle<FbxNode>,
nodes: &FbxNodeContainer,
) -> Result<Self, String> {
if let Ok(content_node) = nodes.get_by_name(video_node_handle, "Content") {
let attrib = content_node.get_attrib(0)?;
let content = match attrib {
FbxAttribute::String(base64) => base64::engine::general_purpose::STANDARD
.decode(base64)
.expect("FBX cannot contain invalid base64-encoded data."),
FbxAttribute::RawData(raw) => raw.clone(),
_ => Default::default(),
};
Ok(Self { content })
} else {
Ok(Self {
content: Default::default(),
})
}
}
}