use std::fmt;
use std::io;
use draco_core::mesh::Mesh;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum FbxWarningCode {
MalformedNullRecord,
MissingNodeEndOffset,
PropertyListLengthMismatch,
NodeEndPastEndOfFile,
UnsupportedTransformInherit,
UnsupportedLayerMapping,
DroppedLayerElement,
NameKeyedObjectModel,
DroppedNodeAttribute,
}
impl FbxWarningCode {
pub fn is_data_loss(self) -> bool {
match self {
FbxWarningCode::MalformedNullRecord
| FbxWarningCode::PropertyListLengthMismatch
| FbxWarningCode::NodeEndPastEndOfFile => false,
FbxWarningCode::MissingNodeEndOffset
| FbxWarningCode::UnsupportedTransformInherit
| FbxWarningCode::UnsupportedLayerMapping
| FbxWarningCode::DroppedLayerElement
| FbxWarningCode::NameKeyedObjectModel
| FbxWarningCode::DroppedNodeAttribute => true,
}
}
pub fn as_str(self) -> &'static str {
match self {
FbxWarningCode::MalformedNullRecord => "malformed-null-record",
FbxWarningCode::MissingNodeEndOffset => "missing-node-end-offset",
FbxWarningCode::PropertyListLengthMismatch => "property-list-length-mismatch",
FbxWarningCode::NodeEndPastEndOfFile => "node-end-past-end-of-file",
FbxWarningCode::UnsupportedTransformInherit => "unsupported-transform-inherit",
FbxWarningCode::UnsupportedLayerMapping => "unsupported-layer-mapping",
FbxWarningCode::DroppedLayerElement => "dropped-layer-element",
FbxWarningCode::NameKeyedObjectModel => "name-keyed-object-model",
FbxWarningCode::DroppedNodeAttribute => "dropped-node-attribute",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FbxWarning {
pub code: FbxWarningCode,
pub message: String,
pub subject: Option<String>,
pub count: u32,
}
impl FbxWarning {
pub fn new(code: FbxWarningCode, message: impl Into<String>) -> Self {
Self {
code,
message: message.into(),
subject: None,
count: 1,
}
}
#[must_use]
pub fn with_subject(mut self, subject: impl Into<String>) -> Self {
self.subject = Some(subject.into());
self
}
}
#[cfg(feature = "fbx-reader")]
pub(crate) fn push_warning(
warnings: &mut Vec<FbxWarning>,
code: FbxWarningCode,
message: String,
subject: Option<&str>,
) {
if let Some(existing) = warnings
.iter_mut()
.find(|warning| warning.code == code && warning.subject.as_deref() == subject)
{
existing.count = existing.count.saturating_add(1);
return;
}
let mut warning = FbxWarning::new(code, message);
warning.subject = subject.map(str::to_owned);
warnings.push(warning);
}
impl fmt::Display for FbxWarning {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}", self.message)?;
if self.count > 1 {
write!(formatter, " (x{})", self.count)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FbxNodeId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FbxTransform {
pub matrix: [[f32; 4]; 4],
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct FbxTransformStack {
pub translation: Option<[f32; 3]>,
pub rotation: Option<[f32; 3]>,
pub scaling: Option<[f32; 3]>,
pub rotation_order: Option<i32>,
pub rotation_active: Option<bool>,
pub pre_rotation: Option<[f32; 3]>,
pub post_rotation: Option<[f32; 3]>,
pub rotation_offset: Option<[f32; 3]>,
pub rotation_pivot: Option<[f32; 3]>,
pub scaling_offset: Option<[f32; 3]>,
pub scaling_pivot: Option<[f32; 3]>,
pub inherit_type: Option<i32>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct FbxGlobalSettings {
pub up_axis: Option<i32>,
pub up_axis_sign: Option<i32>,
pub front_axis: Option<i32>,
pub front_axis_sign: Option<i32>,
pub coord_axis: Option<i32>,
pub coord_axis_sign: Option<i32>,
pub unit_scale_factor: Option<f64>,
pub original_unit_scale_factor: Option<f64>,
pub time_mode: Option<i32>,
}
#[derive(Debug, Clone, Default)]
pub struct FbxMeshLayers {
pub uv_sets: Vec<FbxUvSet>,
pub normal_sets: Vec<FbxNormalSet>,
pub color_sets: Vec<FbxColorSet>,
pub tangent_sets: Vec<FbxTangentSet>,
pub binormal_sets: Vec<FbxBinormalSet>,
pub smoothing_layers: Vec<FbxSmoothingLayer>,
pub crease_layers: Vec<FbxCreaseLayer>,
}
#[derive(Debug, Clone, Default)]
pub struct FbxMeshInstance {
pub name: Option<String>,
pub mesh: Mesh,
pub control_points: Vec<[f32; 3]>,
pub polygon_vertex_indices: Vec<i32>,
pub layers: FbxMeshLayers,
pub edges: Vec<i32>,
pub material_indices: Vec<i32>,
pub skin: Option<FbxSkin>,
pub morph_targets: Vec<FbxMorphTarget>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct FbxLayerSet<const N: usize> {
pub name: Option<String>,
pub mapping: Option<String>,
pub reference: Option<String>,
pub values: Vec<[f32; N]>,
pub indices: Vec<i32>,
}
pub type FbxUvSet = FbxLayerSet<2>;
pub type FbxNormalSet = FbxLayerSet<3>;
pub type FbxColorSet = FbxLayerSet<4>;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct FbxTangentSet {
pub layer: FbxLayerSet<4>,
pub has_handedness: bool,
}
pub type FbxBinormalSet = FbxTangentSet;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum FbxNodeAttribute {
Camera(FbxCamera),
Light(FbxLight),
}
#[derive(Debug, Clone, Default, PartialEq)]
#[non_exhaustive]
pub struct FbxCamera {
pub position: Option<[f32; 3]>,
pub interest_position: Option<[f32; 3]>,
pub up_vector: Option<[f32; 3]>,
pub projection_type: Option<i32>,
pub field_of_view: Option<f32>,
pub field_of_view_x: Option<f32>,
pub field_of_view_y: Option<f32>,
pub focal_length: Option<f32>,
pub near_plane: Option<f32>,
pub far_plane: Option<f32>,
pub aspect_width: Option<f32>,
pub aspect_height: Option<f32>,
pub film_width: Option<f32>,
pub film_height: Option<f32>,
pub film_aspect_ratio: Option<f32>,
pub aperture_mode: Option<i32>,
pub ortho_zoom: Option<f32>,
}
#[derive(Debug, Clone, Default, PartialEq)]
#[non_exhaustive]
pub struct FbxLight {
pub light_type: Option<i32>,
pub color: Option<[f32; 3]>,
pub intensity: Option<f32>,
pub cast_light: Option<bool>,
pub cast_shadows: Option<bool>,
pub decay_type: Option<i32>,
pub decay_start: Option<f32>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FbxSmoothingLayer {
pub mapping: Option<String>,
pub values: Vec<i32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FbxCreaseKind {
Edge,
Vertex,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FbxCreaseLayer {
pub kind: FbxCreaseKind,
pub mapping: Option<String>,
pub values: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct FbxSkinCluster {
pub joint_node_id: FbxNodeId,
pub control_point_indices: Vec<u32>,
pub weights: Vec<f32>,
pub mesh_bind_transform: FbxTransform,
pub joint_bind_transform: FbxTransform,
pub armature_bind_transform: Option<FbxTransform>,
}
#[derive(Debug, Clone)]
pub struct FbxSkin {
pub clusters: Vec<FbxSkinCluster>,
pub bind_pose: Vec<(FbxNodeId, FbxTransform)>,
}
#[derive(Debug, Clone)]
pub struct FbxMorphTarget {
pub name: Option<String>,
pub control_point_indices: Vec<u32>,
pub position_deltas: Vec<[f32; 3]>,
pub normal_deltas: Option<Vec<[f32; 3]>>,
pub default_weight: f32,
pub full_weight: f32,
}
#[derive(Debug, Clone)]
pub struct FbxSceneNode {
pub id: FbxNodeId,
pub name: Option<String>,
pub transform: Option<FbxTransform>,
pub transform_stack: Option<FbxTransformStack>,
pub has_complex_transform_stack: bool,
pub mesh_instances: Vec<FbxMeshInstance>,
pub attribute: Option<FbxNodeAttribute>,
pub children: Vec<FbxSceneNode>,
}
#[cfg(feature = "fbx-reader")]
impl FbxSceneNode {
pub(crate) fn new(name: Option<String>) -> Self {
Self {
id: FbxNodeId(0),
name,
transform: None,
transform_stack: None,
has_complex_transform_stack: false,
mesh_instances: Vec::new(),
attribute: None,
children: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FbxTextureSlot {
Diffuse,
Normal,
Emissive,
Specular,
Roughness,
Metallic,
Ambient,
}
impl FbxTextureSlot {
pub fn property_name(self) -> &'static str {
match self {
FbxTextureSlot::Diffuse => "DiffuseColor",
FbxTextureSlot::Normal => "NormalMap",
FbxTextureSlot::Emissive => "EmissiveColor",
FbxTextureSlot::Specular => "SpecularColor",
FbxTextureSlot::Roughness => "ShininessExponent",
FbxTextureSlot::Metallic => "ReflectionFactor",
FbxTextureSlot::Ambient => "AmbientColor",
}
}
pub fn from_property_name(name: &str) -> Option<Self> {
match name {
"DiffuseColor" => Some(FbxTextureSlot::Diffuse),
"NormalMap" | "Bump" => Some(FbxTextureSlot::Normal),
"EmissiveColor" => Some(FbxTextureSlot::Emissive),
"SpecularColor" => Some(FbxTextureSlot::Specular),
"ShininessExponent" | "Shininess" => Some(FbxTextureSlot::Roughness),
"ReflectionFactor" => Some(FbxTextureSlot::Metallic),
"AmbientColor" => Some(FbxTextureSlot::Ambient),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FbxTextureBinding {
pub slot: FbxTextureSlot,
pub texture_index: usize,
}
#[derive(Debug, Clone, Default)]
pub struct FbxTexture {
pub name: Option<String>,
pub content: Option<Vec<u8>>,
pub filename: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct FbxMaterial {
pub name: Option<String>,
pub shading_model: Option<String>,
pub diffuse: Option<[f32; 3]>,
pub specular: Option<[f32; 3]>,
pub emissive: Option<[f32; 3]>,
pub ambient: Option<[f32; 3]>,
pub diffuse_factor: Option<f32>,
pub specular_factor: Option<f32>,
pub shininess: Option<f32>,
pub emissive_factor: Option<f32>,
pub reflection_factor: Option<f32>,
pub transparency_factor: Option<f32>,
pub opacity: Option<f32>,
pub bump_factor: Option<f32>,
pub textures: Vec<FbxTextureBinding>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FbxAnimChannelPath {
Translation,
Rotation,
Scale,
MorphWeight,
}
impl FbxAnimChannelPath {
pub fn property_name(self) -> &'static str {
match self {
FbxAnimChannelPath::Translation => "Lcl Translation",
FbxAnimChannelPath::Rotation => "Lcl Rotation",
FbxAnimChannelPath::Scale => "Lcl Scaling",
FbxAnimChannelPath::MorphWeight => "DeformPercent",
}
}
pub fn from_property_name(name: &str) -> Option<Self> {
match name {
"Lcl Translation" => Some(FbxAnimChannelPath::Translation),
"Lcl Rotation" => Some(FbxAnimChannelPath::Rotation),
"Lcl Scaling" => Some(FbxAnimChannelPath::Scale),
"DeformPercent" => Some(FbxAnimChannelPath::MorphWeight),
_ => None,
}
}
pub fn component_count(self) -> usize {
match self {
FbxAnimChannelPath::MorphWeight => 1,
_ => 3,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FbxAnimInterpolation {
Step,
Linear,
Cubic,
}
impl FbxAnimInterpolation {
pub fn to_key_attr_flags(self) -> i32 {
match self {
FbxAnimInterpolation::Step => 0x2,
FbxAnimInterpolation::Linear => 0x4,
FbxAnimInterpolation::Cubic => 0x8 | 0x400 | 0x800,
}
}
pub fn from_key_attr_flags(flags: i32) -> Self {
if flags & 0x2 != 0 {
FbxAnimInterpolation::Step
} else if flags & 0x8 != 0 {
FbxAnimInterpolation::Cubic
} else {
FbxAnimInterpolation::Linear
}
}
}
#[derive(Debug, Clone)]
pub struct FbxAnimSampler {
pub input: Vec<f32>,
pub output: Vec<f32>,
pub interpolation: FbxAnimInterpolation,
pub in_tangents: Option<Vec<f32>>,
pub out_tangents: Option<Vec<f32>>,
}
#[derive(Debug, Clone)]
pub struct FbxAnimChannel {
pub node_id: FbxNodeId,
pub node_name: String,
pub path: FbxAnimChannelPath,
pub morph_target_index: Option<u32>,
pub sampler: FbxAnimSampler,
}
#[derive(Debug, Clone)]
pub struct FbxAnimation {
pub name: Option<String>,
pub duration: f32,
pub channels: Vec<FbxAnimChannel>,
}
#[derive(Debug, Clone, Default)]
pub struct FbxScene {
pub global_settings: Option<FbxGlobalSettings>,
pub root_nodes: Vec<FbxSceneNode>,
pub materials: Vec<FbxMaterial>,
pub textures: Vec<FbxTexture>,
pub animations: Vec<FbxAnimation>,
pub warnings: Vec<FbxWarning>,
}
impl FbxScene {
#[cfg(feature = "fbx-reader")]
pub fn from_bytes(bytes: &[u8]) -> io::Result<Self> {
let mut reader = crate::fbx_reader::FbxMemoryReader::from_bytes(bytes)?;
reader.read_scene()
}
#[cfg(feature = "fbx-reader")]
pub fn from_bytes_with_options(
bytes: &[u8],
options: crate::fbx_options::FbxReadOptions,
) -> io::Result<Self> {
let mut reader =
crate::fbx_reader::FbxMemoryReader::from_bytes_with_options(bytes, options)?;
reader.read_scene()
}
#[cfg(feature = "fbx-writer")]
pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
let mut writer = crate::fbx_writer::FbxWriter::new();
writer.add_scene(self)?;
writer.write_to_vec()
}
#[cfg(feature = "fbx-writer")]
pub fn to_ascii_bytes(&self) -> io::Result<Vec<u8>> {
let mut writer =
crate::fbx_writer::FbxWriter::new().with_format(crate::fbx_writer::FbxFormat::Ascii);
writer.add_scene(self)?;
writer.write_to_vec()
}
}