Skip to main content

modelio/
object.rs

1use std::ptr;
2
3use crate::camera::Camera;
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::light::Light;
8use crate::mesh::Mesh;
9use crate::physically_plausible_light::PhysicallyPlausibleLight;
10use crate::protocols::{Component, Named, ObjectContainerComponent};
11use crate::skeleton::Skeleton;
12use crate::types::{BoundingBox, ObjectInfo, ObjectKind};
13use crate::util::{c_string, parse_json, required_handle, take_string};
14use crate::voxel_array::VoxelArray;
15use crate::PackedJointAnimation;
16
17#[derive(Debug, Clone)]
18/// Wraps the corresponding Model I/O object counterpart.
19pub struct Object {
20    handle: ObjectHandle,
21}
22
23impl Named for Object {
24    fn name(&self) -> Option<String> {
25        self.name()
26    }
27
28    fn set_name(&self, name: &str) -> Result<()> {
29        self.set_name(name)
30    }
31}
32
33impl Object {
34    /// Builds this wrapper from the retained handle of the wrapped Model I/O object counterpart.
35    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
36        Self { handle }
37    }
38
39    /// Returns the opaque pointer used to call the wrapped Model I/O object counterpart.
40    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
41        self.handle.as_ptr()
42    }
43
44    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O object counterpart.
45    pub fn new() -> Result<Self> {
46        let mut out_object = ptr::null_mut();
47        let mut out_error = ptr::null_mut();
48        // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
49        let status = unsafe { ffi::mdl_object_new(&mut out_object, &mut out_error) };
50        crate::util::status_result(status, out_error)?;
51        Ok(Self::from_handle(required_handle(out_object, "MDLObject")?))
52    }
53
54    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
55    pub fn info(&self) -> Result<ObjectInfo> {
56        parse_json(
57            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
58            unsafe { ffi::mdl_object_info_json(self.handle.as_ptr()) },
59            "MDLObject",
60        )
61    }
62
63    #[must_use]
64    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
65    pub fn kind(&self) -> ObjectKind {
66        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
67        ObjectKind::from_raw(unsafe { ffi::mdl_object_kind(self.handle.as_ptr()) })
68            .unwrap_or(ObjectKind::Unknown)
69    }
70
71    #[must_use]
72    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
73    pub fn name(&self) -> Option<String> {
74        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
75        take_string(unsafe { ffi::mdl_object_name_string(self.handle.as_ptr()) })
76    }
77
78    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
79    pub fn set_name(&self, name: &str) -> Result<()> {
80        let name = c_string(name)?;
81        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
82        unsafe { ffi::mdl_object_set_name(self.handle.as_ptr(), name.as_ptr()) };
83        Ok(())
84    }
85
86    #[must_use]
87    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
88    pub fn path(&self) -> Option<String> {
89        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
90        take_string(unsafe { ffi::mdl_object_path_string(self.handle.as_ptr()) })
91    }
92
93    #[must_use]
94    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
95    pub fn hidden(&self) -> bool {
96        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
97        unsafe { ffi::mdl_object_hidden(self.handle.as_ptr()) != 0 }
98    }
99
100    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
101    pub fn set_hidden(&self, hidden: bool) {
102        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
103        unsafe { ffi::mdl_object_set_hidden(self.handle.as_ptr(), i32::from(hidden)) };
104    }
105
106    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
107    pub fn add_child(&self, child: &Self) {
108        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
109        unsafe { ffi::mdl_object_add_child(self.handle.as_ptr(), child.handle.as_ptr()) };
110    }
111
112    #[must_use]
113    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
114    pub fn children_container(&self) -> Option<ObjectContainer> {
115        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
116        let ptr = unsafe { ffi::mdl_object_children_container(self.handle.as_ptr()) };
117        // SAFETY: The unsafe operation is valid in this context.
118        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(ObjectContainer::from_handle)
119    }
120
121    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
122    pub fn set_children_container(&self, container: Option<&ObjectContainer>) {
123        // SAFETY: The unsafe operation is valid in this context.
124        unsafe {
125            ffi::mdl_object_set_children_container(
126                self.handle.as_ptr(),
127                container.map_or(ptr::null_mut(), ObjectContainer::as_ptr),
128            );
129        };
130    }
131
132    #[must_use]
133    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
134    pub fn child_count(&self) -> usize {
135        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
136        unsafe { ffi::mdl_object_child_count(self.handle.as_ptr()) as usize }
137    }
138
139    #[must_use]
140    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
141    pub fn child_at(&self, index: usize) -> Option<Self> {
142        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
143        let ptr = unsafe { ffi::mdl_object_child_at(self.handle.as_ptr(), index as u64) };
144        // SAFETY: The unsafe operation is valid in this context.
145        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Self::from_handle)
146    }
147
148    #[must_use]
149    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
150    pub fn children(&self) -> Vec<Self> {
151        (0..self.child_count())
152            .filter_map(|index| self.child_at(index))
153            .collect()
154    }
155
156    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
157    pub fn at_path(&self, path: &str) -> Result<Option<Self>> {
158        let path = c_string(path)?;
159        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
160        let ptr = unsafe { ffi::mdl_object_at_path(self.handle.as_ptr(), path.as_ptr()) };
161        // SAFETY: The unsafe operation is valid in this context.
162        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Self::from_handle))
163    }
164
165    #[must_use]
166    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
167    pub fn bounding_box_at_time(&self, time: f64) -> BoundingBox {
168        let mut min = [0.0_f32; 3];
169        let mut max = [0.0_f32; 3];
170        // SAFETY: The unsafe operation is valid in this context.
171        unsafe {
172            ffi::mdl_object_bounding_box_at_time(
173                self.handle.as_ptr(),
174                time,
175                &mut min[0],
176                &mut min[1],
177                &mut min[2],
178                &mut max[0],
179                &mut max[1],
180                &mut max[2],
181            );
182        }
183        BoundingBox { min, max }
184    }
185
186    #[must_use]
187    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
188    pub fn as_mesh(&self) -> Option<Mesh> {
189        (self.kind() == ObjectKind::Mesh).then(|| Mesh::from_handle(self.handle.clone()))
190    }
191
192    #[must_use]
193    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
194    pub fn as_light(&self) -> Option<Light> {
195        matches!(
196            self.kind(),
197            ObjectKind::Light | ObjectKind::PhysicallyPlausibleLight
198        )
199        .then(|| Light::from_handle(self.handle.clone()))
200    }
201
202    #[must_use]
203    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
204    pub fn as_physically_plausible_light(&self) -> Option<PhysicallyPlausibleLight> {
205        (self.kind() == ObjectKind::PhysicallyPlausibleLight)
206            .then(|| PhysicallyPlausibleLight::from_handle(self.handle.clone()))
207    }
208
209    #[must_use]
210    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
211    pub fn as_camera(&self) -> Option<Camera> {
212        (self.kind() == ObjectKind::Camera).then(|| Camera::from_handle(self.handle.clone()))
213    }
214
215    #[must_use]
216    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
217    pub fn as_voxel_array(&self) -> Option<VoxelArray> {
218        (self.kind() == ObjectKind::VoxelArray)
219            .then(|| VoxelArray::from_handle(self.handle.clone()))
220    }
221
222    #[must_use]
223    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
224    pub fn as_skeleton(&self) -> Option<Skeleton> {
225        (self.kind() == ObjectKind::Skeleton).then(|| Skeleton::from_handle(self.handle.clone()))
226    }
227
228    #[must_use]
229    /// Calls the corresponding Model I/O method on the wrapped Model I/O object counterpart.
230    pub fn as_packed_joint_animation(&self) -> Option<PackedJointAnimation> {
231        (self.kind() == ObjectKind::PackedJointAnimation)
232            .then(|| PackedJointAnimation::from_handle(self.handle.clone()))
233    }
234}
235
236#[derive(Debug, Clone)]
237/// Wraps the corresponding Model I/O object container counterpart.
238pub struct ObjectContainer {
239    handle: ObjectHandle,
240}
241
242impl Component for ObjectContainer {}
243
244impl ObjectContainerComponent for ObjectContainer {
245    fn count(&self) -> usize {
246        self.count()
247    }
248
249    fn object_at(&self, index: usize) -> Option<Object> {
250        self.object_at(index)
251    }
252
253    fn add_object(&self, object: &Object) {
254        self.add_object(object);
255    }
256
257    fn remove_object(&self, object: &Object) {
258        self.remove_object(object);
259    }
260}
261
262impl ObjectContainer {
263    /// Builds this wrapper from the retained handle of the wrapped Model I/O object container counterpart.
264    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
265        Self { handle }
266    }
267
268    /// Returns the opaque pointer used to call the wrapped Model I/O object container counterpart.
269    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
270        self.handle.as_ptr()
271    }
272
273    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O object container counterpart.
274    pub fn new() -> Result<Self> {
275        let mut out_container = ptr::null_mut();
276        let mut out_error = ptr::null_mut();
277        // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
278        let status = unsafe { ffi::mdl_object_container_new(&mut out_container, &mut out_error) };
279        crate::util::status_result(status, out_error)?;
280        Ok(Self::from_handle(required_handle(
281            out_container,
282            "MDLObjectContainer",
283        )?))
284    }
285
286    #[must_use]
287    /// Calls the corresponding Model I/O method on the wrapped Model I/O object container counterpart.
288    pub fn count(&self) -> usize {
289        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
290        unsafe { ffi::mdl_object_container_count(self.handle.as_ptr()) as usize }
291    }
292
293    #[must_use]
294    /// Calls the corresponding Model I/O method on the wrapped Model I/O object container counterpart.
295    pub fn object_at(&self, index: usize) -> Option<Object> {
296        let ptr =
297            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
298            unsafe { ffi::mdl_object_container_object_at(self.handle.as_ptr(), index as u64) };
299        // SAFETY: The unsafe operation is valid in this context.
300        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Object::from_handle)
301    }
302
303    #[must_use]
304    /// Calls the corresponding Model I/O method on the wrapped Model I/O object container counterpart.
305    pub fn objects(&self) -> Vec<Object> {
306        (0..self.count())
307            .filter_map(|index| self.object_at(index))
308            .collect()
309    }
310
311    /// Calls the corresponding Model I/O method on the wrapped Model I/O object container counterpart.
312    pub fn add_object(&self, object: &Object) {
313        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
314        unsafe { ffi::mdl_object_container_add_object(self.handle.as_ptr(), object.as_ptr()) };
315    }
316
317    /// Calls the corresponding Model I/O method on the wrapped Model I/O object container counterpart.
318    pub fn remove_object(&self, object: &Object) {
319        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
320        unsafe { ffi::mdl_object_container_remove_object(self.handle.as_ptr(), object.as_ptr()) };
321    }
322}