1use 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 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 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}