use fbxcel::tree::any::AnyTree;
use fbxscii::{ElementAmphitheatre, ElementParseError, Parser, ParserError};
use std::{
collections::HashMap,
fmt::{Display, Formatter, Result as FmtResult},
io::{BufRead, Read, Seek},
result::Result,
};
use crate::{Object, global::GlobalSettings, object::Objects};
#[derive(Debug, PartialEq)]
pub enum DocumentParseError {
ParserError(ParserError),
BinaryParserError(String),
UnsupportedVersion(u32, Option<String>),
RequiredElementNotFound(String),
ElementParseError(ElementParseError),
PropertyParseError(PropertyParseError),
}
#[derive(Debug, Default)]
pub struct ImportSettings {
pub strict: bool,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Property {
String(String),
Bool(bool),
Int(i32),
Float(f32),
ULongLong(u64),
ILongLong(i64),
Vec3([f32; 3]),
Vec4([f32; 4]),
}
#[derive(Debug)]
pub struct PropertyDetails {
pub name: String,
pub property: Property,
}
#[derive(Debug, PartialEq)]
pub enum PropertyParseError {
InvalidTokenLength(usize, Option<String>),
MissingPropertyType(String),
TokenParseError(String, String),
}
impl Display for PropertyParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
PropertyParseError::InvalidTokenLength(len, property_type) => write!(
f,
"Invalid token length: {} for property type: {}",
len,
property_type.as_ref().unwrap_or(&String::new())
),
PropertyParseError::MissingPropertyType(property_type) => {
write!(f, "Missing property type: {}", property_type)
}
PropertyParseError::TokenParseError(property_type, token) => write!(
f,
"Token parse does not match property type: {} for token: {}",
property_type, token
),
}
}
}
impl std::error::Error for PropertyParseError {}
pub type Template = HashMap<String, Property>;
#[derive(Debug, PartialEq, Clone)]
pub struct LazyObject {
pub name: String,
pub type_name: String,
pub class_name: String,
pub element_index: usize,
}
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub struct ObjectPropertyConnection {
pub dest: u64,
pub property: String,
}
#[derive(Default, Debug, Clone)]
pub struct Document {
pub(crate) fbx_version: u32,
pub(crate) creator: String,
pub(crate) creation_date: [u32; 7],
pub(crate) templates: HashMap<String, Template>,
pub(crate) default_template_by_object_type: HashMap<String, String>,
pub(crate) global_settings: Template,
pub(crate) object_element_amphitheatre: ElementAmphitheatre,
pub(crate) objects: HashMap<u64, LazyObject>,
pub(crate) object_connections: HashMap<u64, Vec<u64>>,
pub(crate) object_property_connections: HashMap<u64, Vec<ObjectPropertyConnection>>,
pub(crate) property_connections:
HashMap<ObjectPropertyConnection, Vec<ObjectPropertyConnection>>,
pub(crate) object_to_source_properties: HashMap<u64, Vec<String>>,
}
impl Document {
pub fn version(&self) -> u32 {
self.fbx_version
}
pub fn creator(&self) -> &str {
&self.creator
}
pub fn creation_date(&self) -> &[u32; 7] {
&self.creation_date
}
pub fn global_settings(&self) -> GlobalSettings<'_> {
GlobalSettings::new(self, &self.global_settings)
}
pub fn objects(&self) -> Objects<'_> {
Objects {
iter: self.objects.iter(),
document: self,
}
}
pub fn object_by_index(&self, index: u64) -> Option<Object<'_>> {
let object = self.object_for_index(index)?;
self.template_for_object(object)
.map(|template| Object::new(self, template, object, index))
}
pub(crate) fn object_for_index(&self, index: u64) -> Option<&LazyObject> {
self.objects.get(&index)
}
pub(crate) fn template_for_object(&self, object: &LazyObject) -> Option<&Template> {
self.templates.get(&object.type_name).or_else(|| {
self.default_template_by_object_type
.get(&object.type_name)
.and_then(|full_key| self.templates.get(full_key))
})
}
pub fn from_parser<R>(
parser: Parser<R>,
settings: ImportSettings,
) -> Result<Self, DocumentParseError>
where
R: BufRead,
{
let elements = parser.load().map_err(DocumentParseError::ParserError)?;
let mut document = Self::default();
elements.load_into_document(&mut document, settings)?;
Ok(document)
}
pub fn from_binary_reader<R>(
reader: R,
settings: ImportSettings,
) -> Result<Self, DocumentParseError>
where
R: Read + Seek,
{
let any_tree = AnyTree::from_seekable_reader(reader)
.map_err(|error| DocumentParseError::BinaryParserError(error.to_string()))?;
let mut document = Self::default();
any_tree.load_into_document(&mut document, settings)?;
Ok(document)
}
}
pub trait DocumentLoader {
fn load_into_document(
self,
document: &mut Document,
settings: ImportSettings,
) -> Result<(), DocumentParseError>;
}