Skip to main content

fbx_dom/objects/
layered_texture.rs

1//! FBX `LayeredTexture` — Assimp [`LayeredTexture`](https://github.com/assimp/assimp/blob/master/code/AssetLib/FBX/FBXDocument.h).
2
3use std::convert::TryFrom;
4
5use crate::{OwnedDocument, OwnedObject};
6
7use super::{AttrExtractorExt, FbxObjectTag, FbxTypeMismatch, Texture, fbx_object_tag};
8
9const BLEND_MODES_ATTR: &str = "BlendModes";
10const ALPHAS_ATTR: &str = "Alphas";
11
12#[derive(Debug, PartialEq)]
13pub struct LayeredTexture {
14    object: OwnedObject,
15    pub blend_mode: i32,
16    pub alpha: f32,
17}
18
19impl LayeredTexture {
20    pub fn inner(&self) -> &OwnedObject {
21        &self.object
22    }
23
24    pub fn into_inner(self) -> OwnedObject {
25        self.object
26    }
27
28    pub fn blend_mode(&self) -> i32 {
29        self.blend_mode
30    }
31
32    pub fn alpha(&self) -> f32 {
33        self.alpha
34    }
35
36    /// Resolve incoming `Texture -> LayeredTexture` links.
37    pub fn get_textures<'a>(&'a self, document: &'a OwnedDocument) -> Vec<&'a Texture> {
38        let layered_texture_id = self.inner().object_index;
39        document
40            .textures
41            .iter()
42            .filter(|texture| {
43                texture
44                    .inner()
45                    .connected_object_ids
46                    .contains(&layered_texture_id)
47            })
48            .collect()
49    }
50
51    pub fn texture_count(&self, document: &OwnedDocument) -> usize {
52        self.get_textures(document).len()
53    }
54}
55
56impl TryFrom<OwnedObject> for LayeredTexture {
57    type Error = FbxTypeMismatch;
58
59    fn try_from(o: OwnedObject) -> Result<Self, Self::Error> {
60        match fbx_object_tag(&o) {
61            FbxObjectTag::LayeredTexture => {
62                let blend_mode = match o
63                    .attributes
64                    .optional_token_case_insensitive(BLEND_MODES_ATTR)
65                {
66                    Ok(v) => v.and_then(|x| x.trim().parse::<i32>().ok()).unwrap_or(0),
67                    Err(reason) => return Err(FbxTypeMismatch { object: o, reason }),
68                };
69                let alpha = match o.attributes.optional_token_case_insensitive(ALPHAS_ATTR) {
70                    Ok(v) => v.and_then(|x| x.trim().parse::<f32>().ok()).unwrap_or(1.0),
71                    Err(reason) => return Err(FbxTypeMismatch { object: o, reason }),
72                };
73                Ok(LayeredTexture {
74                    object: o,
75                    blend_mode,
76                    alpha,
77                })
78            }
79            _ => Err(FbxTypeMismatch::wrong_object_kind(
80                o,
81                "LayeredTexture".to_string(),
82            )),
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use std::collections::HashMap;
90    use std::convert::TryFrom;
91
92    use fbxscii::{ElementAttribute, LeafAttribute};
93
94    use super::*;
95    use crate::Property;
96    use crate::objects::{
97        LAYERED_TEXTURE_CLASS_NAME, LAYERED_TEXTURE_TYPE_NAME, TEXTURE_CLASS_NAME,
98        TEXTURE_TYPE_NAME,
99    };
100
101    fn leaf(tokens: &[&str]) -> ElementAttribute {
102        ElementAttribute::Leaf(Box::new(LeafAttribute {
103            key: String::new(),
104            tokens: tokens.iter().map(|s| (*s).to_string()).collect(),
105        }))
106    }
107
108    #[test]
109    fn parses_blend_mode_and_alpha() {
110        let layered = LayeredTexture::try_from(OwnedObject {
111            object_index: 900,
112            name: "LayeredTexture::A".into(),
113            type_name: LAYERED_TEXTURE_TYPE_NAME.into(),
114            class_name: LAYERED_TEXTURE_CLASS_NAME.into(),
115            properties: HashMap::new(),
116            attributes: HashMap::from([
117                (BLEND_MODES_ATTR.to_string(), leaf(&["1"])),
118                (ALPHAS_ATTR.to_string(), leaf(&["0.25"])),
119            ]),
120            connected_object_ids: vec![],
121            object_property_targets: vec![],
122            pp_property_targets: HashMap::new(),
123        })
124        .unwrap();
125        assert_eq!(layered.blend_mode(), 1);
126        assert_eq!(layered.alpha(), 0.25);
127    }
128
129    #[test]
130    fn defaults_blend_mode_and_alpha() {
131        let layered = LayeredTexture::try_from(OwnedObject {
132            object_index: 901,
133            name: "LayeredTexture::B".into(),
134            type_name: LAYERED_TEXTURE_TYPE_NAME.into(),
135            class_name: LAYERED_TEXTURE_CLASS_NAME.into(),
136            properties: HashMap::new(),
137            attributes: HashMap::new(),
138            connected_object_ids: vec![],
139            object_property_targets: vec![],
140            pp_property_targets: HashMap::new(),
141        })
142        .unwrap();
143        assert_eq!(layered.blend_mode(), 0);
144        assert_eq!(layered.alpha(), 1.0);
145    }
146
147    #[test]
148    fn resolves_textures_and_count() {
149        let layered = LayeredTexture::try_from(OwnedObject {
150            object_index: 902,
151            name: "LayeredTexture::C".into(),
152            type_name: LAYERED_TEXTURE_TYPE_NAME.into(),
153            class_name: LAYERED_TEXTURE_CLASS_NAME.into(),
154            properties: HashMap::new(),
155            attributes: HashMap::new(),
156            connected_object_ids: vec![],
157            object_property_targets: vec![],
158            pp_property_targets: HashMap::new(),
159        })
160        .unwrap();
161        let texture = Texture::try_from(OwnedObject {
162            object_index: 903,
163            name: "Texture::A".into(),
164            type_name: TEXTURE_TYPE_NAME.into(),
165            class_name: TEXTURE_CLASS_NAME.into(),
166            properties: HashMap::from([("Foo".into(), Property::Int(1))]),
167            attributes: HashMap::new(),
168            connected_object_ids: vec![902],
169            object_property_targets: vec![],
170            pp_property_targets: HashMap::new(),
171        })
172        .unwrap();
173        let mut owned = OwnedDocument::default();
174        owned.textures = vec![texture];
175        let links = layered.get_textures(&owned);
176        assert_eq!(links.len(), 1);
177        assert_eq!(links[0].inner().object_index, 903);
178        assert_eq!(layered.texture_count(&owned), 1);
179    }
180}