fbxcel_dom/v7400/object.rs
1//! Objects-related stuff.
2//!
3//! # Object
4//!
5//! There are some data associated to objects.
6//!
7//! ## Object metadata
8//!
9//! Each object node has object metadata.
10//! A metadata consists of four elements:
11//!
12//! ### Object ID
13//!
14//! Each object has object ID, which is unique in the document.
15//!
16//! Some objects have ID but have no corresponding nodes (in low-level tree).
17//! Usually they are safely ignorable.
18//!
19//! This is represented by [`ObjectId`], and can be retrieved by
20//! [`ObjectHandle::object_id()`].
21//!
22//! ### Name
23//!
24//! Objects may have names.
25//! Note that the name can be empty string, and not guaranteed to be unique.
26//!
27//! This can be retrieved by [`ObjectHandle::name()`].
28//!
29//! ### Class and subclass
30//!
31//! These are used to distinguish actual data type (usage) of the object.
32//!
33//! It is not users' business to know their mappings, so `fbxcel_dom`
34//! provides [`ObjectHandle::get_typed()`] to automatically cast the
35//! object handle to "usable" handle types.
36//!
37//! However, `fbxcel_dom` is not perfect and would miss many mappings (because
38//! FBX is proprietary format, and specification is not open).
39//! If users want to use object types which are unsupported by `fbxcel_dom`,
40//! they can implement new object handle types by their own, and/or use
41//! lower-level APIs (such as [`ObjectHandle::source_objects()`],
42//! [`ObjectHandle::destination_objects()`], and `fbxcel::tree` APIs).
43//!
44//! They can be retrieved by [`ObjectHandle::class()`] and
45//! [`ObjectHandle::subclass()`].
46//!
47//! ## Object node ID
48//!
49//! If an object has corresponding tree node, then the node ID for the object
50//! can be represented as "object node ID".
51//!
52//! This is represented by [`ObjectNodeId`].
53//!
54//! ## Object handle
55//!
56//! Object node ID is not useful without the document the object belongs to.
57//! Object handle is a struct which contains a document and object identifiers
58//! (object ID and object node ID for the same object).
59//!
60//! This is represented by [`ObjectHandle`].
61//!
62//! [`ObjectId`]: struct.ObjectId.html
63//! [`ObjectHandle`]: struct.ObjectHandle.html
64//! [`ObjectHandle::class()`]: struct.ObjectHandle.html#method.class
65//! [`ObjectHandle::destination_objects()`]:
66//! struct.ObjectHandle.html#method.destination_objects
67//! [`ObjectHandle::get_typed()`]: struct.ObjectHandle.html#method.get_typed
68//! [`ObjectHandle::name()`]: struct.ObjectHandle.html#method.name
69//! [`ObjectHandle::object_id()`]: struct.ObjectHandle.html#method.object_id
70//! [`ObjectHandle::source_objects()`]:
71//! struct.ObjectHandle.html#method.source_objects
72//! [`ObjectHandle::subclass()`]: struct.ObjectHandle.html#method.subclass
73//! [`ObjectNodeId`]: struct.ObjectNodeId.html
74
75use std::fmt;
76
77use fbxcel::tree::v7400::{NodeHandle, NodeId};
78
79use crate::v7400::{connection::Connection, Document};
80
81use self::property::{ObjectProperties, PropertiesHandle};
82pub use self::typed::TypedObjectHandle;
83pub(crate) use self::{
84 cache::ObjectsCache,
85 meta::{ObjectClassSym, ObjectMeta},
86};
87
88#[macro_use]
89mod macros;
90
91mod cache;
92pub mod deformer;
93pub mod geometry;
94pub mod material;
95mod meta;
96pub mod model;
97pub mod nodeattribute;
98pub mod property;
99pub mod scene;
100pub mod texture;
101mod typed;
102pub mod video;
103
104/// Node ID of a object node.
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106pub struct ObjectNodeId(NodeId);
107
108impl ObjectNodeId {
109 /// Creates a new `ObjectNodeId`.
110 pub(crate) fn new(node_id: NodeId) -> Self {
111 Self(node_id)
112 }
113
114 /// Creates a new `ObjectHandle`.
115 pub fn to_object_handle(self, doc: &Document) -> ObjectHandle<'_> {
116 ObjectHandle::from_object_node_id(self, doc)
117 }
118}
119
120impl std::ops::Deref for ObjectNodeId {
121 type Target = NodeId;
122
123 fn deref(&self) -> &Self::Target {
124 &self.0
125 }
126}
127
128impl From<ObjectNodeId> for NodeId {
129 fn from(v: ObjectNodeId) -> Self {
130 v.0
131 }
132}
133
134/// Object ID.
135#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
136pub struct ObjectId(i64);
137
138impl ObjectId {
139 /// Creates a new `ObjectId`.
140 pub(crate) fn new(id: i64) -> Self {
141 Self(id)
142 }
143
144 /// Creates a new `ObjectHandle`.
145 pub fn to_object_handle(self, doc: &Document) -> Option<ObjectHandle<'_>> {
146 ObjectHandle::from_object_id(self, doc)
147 }
148
149 /// Returns an iterator of destination objects and connection labels.
150 pub fn destination_objects(
151 self,
152 doc: &Document,
153 ) -> impl Iterator<Item = ConnectedObjectHandle<'_>> {
154 doc.connections_cache()
155 .outgoing_connections(self)
156 .map(move |conn| ConnectedObjectHandle::new(conn.destination_id(), conn, doc))
157 }
158
159 /// Returns an iterator of source objects and connection labels.
160 pub fn source_objects(self, doc: &Document) -> impl Iterator<Item = ConnectedObjectHandle<'_>> {
161 doc.connections_cache()
162 .incoming_connections(self)
163 .map(move |conn| ConnectedObjectHandle::new(conn.source_id(), conn, doc))
164 }
165
166 /// Returns the raw object ID.
167 pub fn raw(self) -> i64 {
168 self.0
169 }
170}
171
172/// Object handle.
173///
174/// See the [module-level documentation](index.html) for more detail.
175#[derive(Clone, Copy)]
176pub struct ObjectHandle<'a> {
177 /// Node ID.
178 node_id: ObjectNodeId,
179 /// Object metadata.
180 object_meta: &'a ObjectMeta,
181 /// Document.
182 doc: &'a Document,
183}
184
185impl<'a> ObjectHandle<'a> {
186 /// Creates a new `ObjectHandle` from the given object node ID.
187 ///
188 /// # Panics
189 ///
190 /// This may panic if the object node with the given ID does not exist in
191 /// the given document.
192 fn from_object_node_id(node_id: ObjectNodeId, doc: &'a Document) -> Self {
193 let object_meta = doc
194 .objects_cache()
195 .meta_from_node_id(node_id)
196 .unwrap_or_else(|| panic!("No corresponding object metadata: node_id={:?}", node_id));
197 Self {
198 node_id,
199 object_meta,
200 doc,
201 }
202 }
203
204 /// Creates a new `ObjectHandle` from the given object node ID.
205 ///
206 /// Returns `None` if the given object ID has no corresponding FBX node.
207 fn from_object_id(obj_id: ObjectId, doc: &'a Document) -> Option<Self> {
208 let node_id = doc.objects_cache().node_id(obj_id)?;
209 let object_meta = doc
210 .objects_cache()
211 .meta_from_node_id(node_id)
212 .expect("Should never fail: object cache should be consistent");
213 assert_eq!(obj_id, object_meta.object_id(), "Object ID mismatch");
214 Some(Self {
215 node_id,
216 object_meta,
217 doc,
218 })
219 }
220
221 /// Returns object node ID.
222 pub fn object_node_id(&self) -> ObjectNodeId {
223 self.node_id
224 }
225
226 /// Returns object ID.
227 pub fn object_id(&self) -> ObjectId {
228 self.object_meta.object_id()
229 }
230
231 /// Returns a reference to the document.
232 pub fn document(&self) -> &'a Document {
233 self.doc
234 }
235
236 /// Returns the node handle.
237 pub fn node(&self) -> NodeHandle<'a> {
238 self.node_id.to_handle(self.doc.tree())
239 }
240
241 /// Returns the object type.
242 pub fn get_typed(&self) -> TypedObjectHandle<'a> {
243 TypedObjectHandle::new(*self)
244 }
245
246 /// Returns object name.
247 pub fn name(&self) -> Option<&'a str> {
248 self.object_meta.name()
249 }
250
251 /// Returns object class.
252 pub fn class(&self) -> &'a str {
253 self.doc
254 .objects_cache()
255 .resolve_class_string(self.object_meta.class_sym())
256 }
257
258 /// Returns object subclass.
259 pub fn subclass(&self) -> &'a str {
260 self.doc
261 .objects_cache()
262 .resolve_class_string(self.object_meta.subclass_sym())
263 }
264
265 /// Returns an iterator of destination objects and connection labels.
266 pub fn destination_objects(&self) -> impl Iterator<Item = ConnectedObjectHandle<'a>> {
267 self.object_id().destination_objects(self.doc)
268 }
269
270 /// Returns an iterator of source objects and connection labels.
271 pub fn source_objects(&self) -> impl Iterator<Item = ConnectedObjectHandle<'a>> {
272 self.object_id().source_objects(self.doc)
273 }
274
275 /// Returns a handle of the directly associated properties node.
276 pub fn direct_properties(&self) -> Option<PropertiesHandle<'a>> {
277 PropertiesHandle::from_object(self)
278 }
279
280 /// Returns a proxy to object properties using the given native typename.
281 ///
282 /// `native_typename` should be the value of the first attribute of
283 /// the `PropertyTemplate` node to be used.
284 pub fn properties_by_native_typename(&self, native_typename: &str) -> ObjectProperties<'a> {
285 ObjectProperties::from_object(self, native_typename)
286 }
287}
288
289impl fmt::Debug for ObjectHandle<'_> {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 /// Object metadata type for debug printing.
292 #[derive(Debug)]
293 #[allow(dead_code)] // Fields are intended for being printed by `Debug`.
294 struct ObjectMeta<'a> {
295 /// Object ID.
296 id: ObjectId,
297 /// Name (if exists).
298 name: Option<&'a str>,
299 /// Class.
300 class: &'a str,
301 /// Subclass.
302 subclass: &'a str,
303 }
304 let meta = ObjectMeta {
305 id: self.object_id(),
306 name: self.name(),
307 class: self.class(),
308 subclass: self.subclass(),
309 };
310 f.debug_struct("ObjectHandle")
311 .field("node_id", &self.node_id)
312 .field("meta", &meta)
313 .finish()
314 }
315}
316
317/// Object handle (or ID) for connected object.
318#[derive(Debug, Clone, Copy)]
319pub struct ConnectedObjectHandle<'a> {
320 /// Connected object.
321 object_id: ObjectId,
322 /// Connection.
323 connection: &'a Connection,
324 /// Document.
325 doc: &'a Document,
326}
327
328impl<'a> ConnectedObjectHandle<'a> {
329 /// Creates a new `ConnectedObjectHandle`.
330 fn new(object_id: ObjectId, connection: &'a Connection, doc: &'a Document) -> Self {
331 Self {
332 object_id,
333 connection,
334 doc,
335 }
336 }
337
338 /// Returns object ID.
339 pub fn object_id(&self) -> ObjectId {
340 self.object_id
341 }
342
343 /// Returns object handle if corresponding object node is available.
344 pub fn object_handle(&self) -> Option<ObjectHandle<'a>> {
345 self.object_id.to_object_handle(self.doc)
346 }
347
348 /// Returns connection label if available.
349 pub fn label(&self) -> Option<&'a str> {
350 self.connection
351 .label_sym()
352 .map(|sym| self.doc.connections_cache().resolve_label(sym))
353 }
354}