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}