Skip to main content

fbx_dom/objects/
camera.rs

1//! FBX `NodeAttribute` / `Camera` — Assimp [`Camera`](https://github.com/assimp/assimp/blob/master/code/AssetLib/FBX/FBXDocument.h).
2
3use std::collections::HashMap;
4use std::convert::TryFrom;
5
6use crate::{OwnedObject, Property};
7
8use super::{FbxObjectTag, FbxTypeMismatch, fbx_object_tag};
9
10const PROP_POSITION: &str = "Position";
11const PROP_UP_VECTOR: &str = "UpVector";
12const PROP_INTEREST_POSITION: &str = "InterestPosition";
13const PROP_ASPECT_WIDTH: &str = "AspectWidth";
14const PROP_ASPECT_HEIGHT: &str = "AspectHeight";
15const PROP_FILM_WIDTH: &str = "FilmWidth";
16const PROP_FILM_HEIGHT: &str = "FilmHeight";
17const PROP_NEAR_PLANE: &str = "NearPlane";
18const PROP_FAR_PLANE: &str = "FarPlane";
19const PROP_FILM_ASPECT_RATIO: &str = "FilmAspectRatio";
20const PROP_APERTURE_MODE: &str = "ApertureMode";
21const PROP_FIELD_OF_VIEW: &str = "FieldOfView";
22const PROP_FOCAL_LENGTH: &str = "FocalLength";
23
24const DEFAULT_FOV_UNKNOWN: f32 = -1.0;
25
26#[derive(Debug, PartialEq)]
27pub struct Camera(pub OwnedObject);
28
29impl Camera {
30    pub fn inner(&self) -> &OwnedObject {
31        &self.0
32    }
33
34    pub fn into_inner(self) -> OwnedObject {
35        self.0
36    }
37
38    /// Temporary bridge to Assimp-style camera properties until typed accessors are added.
39    pub fn properties(&self) -> &HashMap<String, Property> {
40        &self.0.properties
41    }
42
43    pub fn property(&self, name: &str) -> Option<&Property> {
44        self.0.properties.get(name)
45    }
46
47    // Assimp parity (`FBXDocument.h` camera accessors) from NodeAttribute property table.
48    pub fn position(&self) -> [f32; 3] {
49        match self.property(PROP_POSITION) {
50            Some(Property::Vec3(v)) => *v,
51            _ => [0.0, 0.0, 0.0],
52        }
53    }
54
55    pub fn up_vector(&self) -> [f32; 3] {
56        match self.property(PROP_UP_VECTOR) {
57            Some(Property::Vec3(v)) => *v,
58            _ => [0.0, 1.0, 0.0],
59        }
60    }
61
62    pub fn interest_position(&self) -> [f32; 3] {
63        match self.property(PROP_INTEREST_POSITION) {
64            Some(Property::Vec3(v)) => *v,
65            _ => [0.0, 0.0, 0.0],
66        }
67    }
68
69    pub fn aspect_width(&self) -> f32 {
70        match self.property(PROP_ASPECT_WIDTH) {
71            Some(Property::Float(v)) => *v,
72            _ => 1.0,
73        }
74    }
75
76    pub fn aspect_height(&self) -> f32 {
77        match self.property(PROP_ASPECT_HEIGHT) {
78            Some(Property::Float(v)) => *v,
79            _ => 1.0,
80        }
81    }
82
83    pub fn film_width(&self) -> f32 {
84        match self.property(PROP_FILM_WIDTH) {
85            Some(Property::Float(v)) => *v,
86            _ => 1.0,
87        }
88    }
89
90    pub fn film_height(&self) -> f32 {
91        match self.property(PROP_FILM_HEIGHT) {
92            Some(Property::Float(v)) => *v,
93            _ => 1.0,
94        }
95    }
96
97    pub fn near_plane(&self) -> f32 {
98        match self.property(PROP_NEAR_PLANE) {
99            Some(Property::Float(v)) => *v,
100            _ => 0.1,
101        }
102    }
103
104    pub fn far_plane(&self) -> f32 {
105        match self.property(PROP_FAR_PLANE) {
106            Some(Property::Float(v)) => *v,
107            _ => 100.0,
108        }
109    }
110
111    pub fn film_aspect_ratio(&self) -> f32 {
112        match self.property(PROP_FILM_ASPECT_RATIO) {
113            Some(Property::Float(v)) => *v,
114            _ => 1.0,
115        }
116    }
117
118    pub fn aperture_mode(&self) -> i32 {
119        match self.property(PROP_APERTURE_MODE) {
120            Some(Property::Int(v)) => *v,
121            _ => 0,
122        }
123    }
124
125    pub fn field_of_view(&self) -> f32 {
126        match self.property(PROP_FIELD_OF_VIEW) {
127            Some(Property::Float(v)) => *v,
128            _ => DEFAULT_FOV_UNKNOWN,
129        }
130    }
131
132    pub fn focal_length(&self) -> f32 {
133        match self.property(PROP_FOCAL_LENGTH) {
134            Some(Property::Float(v)) => *v,
135            _ => 1.0,
136        }
137    }
138}
139
140impl TryFrom<OwnedObject> for Camera {
141    type Error = FbxTypeMismatch;
142
143    fn try_from(o: OwnedObject) -> Result<Self, Self::Error> {
144        match fbx_object_tag(&o) {
145            FbxObjectTag::Camera => Ok(Camera(o)),
146            _ => Err(FbxTypeMismatch::wrong_object_kind(o, "Camera".to_string())),
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use std::collections::HashMap;
154    use std::convert::TryFrom;
155
156    use crate::objects::{NODE_ATTRIBUTE_CAMERA_CLASS_NAME, NODE_ATTRIBUTE_TYPE_NAME};
157    use crate::{OwnedObject, Property};
158
159    use super::Camera;
160
161    #[test]
162    fn property_accessors_return_owned_object_properties() {
163        let mut properties = HashMap::new();
164        properties.insert("NearPlane".to_string(), Property::Float(0.25));
165        let o = OwnedObject {
166            object_index: 99,
167            name: "Camera".into(),
168            type_name: NODE_ATTRIBUTE_TYPE_NAME.into(),
169            class_name: NODE_ATTRIBUTE_CAMERA_CLASS_NAME.into(),
170            properties,
171            attributes: HashMap::new(),
172            connected_object_ids: vec![],
173            object_property_targets: vec![],
174            pp_property_targets: HashMap::new(),
175        };
176        let camera = Camera::try_from(o).unwrap();
177        assert_eq!(camera.property("NearPlane"), Some(&Property::Float(0.25)));
178        assert_eq!(camera.properties().len(), 1);
179    }
180
181    #[test]
182    fn typed_accessors_return_defaults() {
183        let o = OwnedObject {
184            object_index: 99,
185            name: "Camera".into(),
186            type_name: NODE_ATTRIBUTE_TYPE_NAME.into(),
187            class_name: NODE_ATTRIBUTE_CAMERA_CLASS_NAME.into(),
188            properties: HashMap::new(),
189            attributes: HashMap::new(),
190            connected_object_ids: vec![],
191            object_property_targets: vec![],
192            pp_property_targets: HashMap::new(),
193        };
194        let camera = Camera::try_from(o).unwrap();
195        assert_eq!(camera.position(), [0.0, 0.0, 0.0]);
196        assert_eq!(camera.up_vector(), [0.0, 1.0, 0.0]);
197        assert_eq!(camera.interest_position(), [0.0, 0.0, 0.0]);
198        assert_eq!(camera.aspect_width(), 1.0);
199        assert_eq!(camera.aspect_height(), 1.0);
200        assert_eq!(camera.film_width(), 1.0);
201        assert_eq!(camera.film_height(), 1.0);
202        assert_eq!(camera.near_plane(), 0.1);
203        assert_eq!(camera.far_plane(), 100.0);
204        assert_eq!(camera.film_aspect_ratio(), 1.0);
205        assert_eq!(camera.aperture_mode(), 0);
206        assert_eq!(camera.field_of_view(), -1.0);
207        assert_eq!(camera.focal_length(), 1.0);
208    }
209
210    #[test]
211    fn typed_accessors_return_property_values() {
212        let mut properties = HashMap::new();
213        properties.insert("Position".to_string(), Property::Vec3([1.0, 2.0, 3.0]));
214        properties.insert("UpVector".to_string(), Property::Vec3([0.0, 0.0, 1.0]));
215        properties.insert(
216            "InterestPosition".to_string(),
217            Property::Vec3([4.0, 5.0, 6.0]),
218        );
219        properties.insert("AspectWidth".to_string(), Property::Float(16.0));
220        properties.insert("AspectHeight".to_string(), Property::Float(9.0));
221        properties.insert("FilmWidth".to_string(), Property::Float(0.825));
222        properties.insert("FilmHeight".to_string(), Property::Float(0.446));
223        properties.insert("NearPlane".to_string(), Property::Float(0.25));
224        properties.insert("FarPlane".to_string(), Property::Float(500.0));
225        properties.insert("FilmAspectRatio".to_string(), Property::Float(1.777_777_8));
226        properties.insert("ApertureMode".to_string(), Property::Int(1));
227        properties.insert("FieldOfView".to_string(), Property::Float(60.0));
228        properties.insert("FocalLength".to_string(), Property::Float(35.0));
229        let o = OwnedObject {
230            object_index: 99,
231            name: "Camera".into(),
232            type_name: NODE_ATTRIBUTE_TYPE_NAME.into(),
233            class_name: NODE_ATTRIBUTE_CAMERA_CLASS_NAME.into(),
234            properties,
235            attributes: HashMap::new(),
236            connected_object_ids: vec![],
237            object_property_targets: vec![],
238            pp_property_targets: HashMap::new(),
239        };
240        let camera = Camera::try_from(o).unwrap();
241        assert_eq!(camera.position(), [1.0, 2.0, 3.0]);
242        assert_eq!(camera.up_vector(), [0.0, 0.0, 1.0]);
243        assert_eq!(camera.interest_position(), [4.0, 5.0, 6.0]);
244        assert_eq!(camera.aspect_width(), 16.0);
245        assert_eq!(camera.aspect_height(), 9.0);
246        assert_eq!(camera.film_width(), 0.825);
247        assert_eq!(camera.film_height(), 0.446);
248        assert_eq!(camera.near_plane(), 0.25);
249        assert_eq!(camera.far_plane(), 500.0);
250        assert_eq!(camera.film_aspect_ratio(), 1.777_777_8);
251        assert_eq!(camera.aperture_mode(), 1);
252        assert_eq!(camera.field_of_view(), 60.0);
253        assert_eq!(camera.focal_length(), 35.0);
254    }
255}