Skip to main content

modelio/
asset.rs

1use std::path::Path;
2use std::ptr;
3
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::mesh::Mesh;
8use crate::object::Object;
9use crate::types::{AssetInfo, BoundingBox};
10use crate::util::{c_string, parse_json, path_to_c_string, required_handle, take_string};
11
12#[derive(Debug, Clone)]
13/// Wraps the corresponding Model I/O asset counterpart.
14pub struct Asset {
15    handle: ObjectHandle,
16}
17
18impl Asset {
19    /// Builds this wrapper from the retained handle of the wrapped Model I/O asset counterpart.
20    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
21        Self { handle }
22    }
23
24    /// Returns the opaque pointer used to call the wrapped Model I/O asset counterpart.
25    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
26        self.handle.as_ptr()
27    }
28
29    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O asset counterpart.
30    pub fn new() -> Result<Self> {
31        let mut out_asset = ptr::null_mut();
32        let mut out_error = ptr::null_mut();
33        // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
34        let status = unsafe { ffi::mdl_asset_new_empty(&mut out_asset, &mut out_error) };
35        crate::util::status_result(status, out_error)?;
36        Ok(Self::from_handle(required_handle(out_asset, "MDLAsset")?))
37    }
38
39    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
40    pub fn from_url(path: impl AsRef<Path>) -> Result<Self> {
41        let path = path_to_c_string(path.as_ref())?;
42        let mut out_asset = ptr::null_mut();
43        let mut out_error = ptr::null_mut();
44        let status =
45            // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
46            unsafe { ffi::mdl_asset_new_with_url(path.as_ptr(), &mut out_asset, &mut out_error) };
47        crate::util::status_result(status, out_error)?;
48        Ok(Self::from_handle(required_handle(out_asset, "MDLAsset")?))
49    }
50
51    #[must_use]
52    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
53    pub fn can_import_file_extension(path_extension: &str) -> bool {
54        let trimmed = path_extension.trim_start_matches('.');
55        crate::util::c_string(trimmed)
56            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
57            .is_ok_and(|ext| unsafe { ffi::mdl_asset_can_import_file_extension(ext.as_ptr()) != 0 })
58    }
59
60    #[must_use]
61    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
62    pub fn can_export_file_extension(path_extension: &str) -> bool {
63        let trimmed = path_extension.trim_start_matches('.');
64        crate::util::c_string(trimmed)
65            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
66            .is_ok_and(|ext| unsafe { ffi::mdl_asset_can_export_file_extension(ext.as_ptr()) != 0 })
67    }
68
69    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
70    pub fn info(&self) -> Result<AssetInfo> {
71        parse_json(
72            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
73            unsafe { ffi::mdl_asset_info_json(self.handle.as_ptr()) },
74            "MDLAsset",
75        )
76    }
77
78    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
79    pub fn export_to_url(&self, path: impl AsRef<Path>) -> Result<()> {
80        let path = path_to_c_string(path.as_ref())?;
81        let mut out_error = ptr::null_mut();
82        // SAFETY: The unsafe operation is valid in this context.
83        let status = unsafe {
84            ffi::mdl_asset_export_to_url(self.handle.as_ptr(), path.as_ptr(), &mut out_error)
85        };
86        crate::util::status_result(status, out_error)
87    }
88
89    #[must_use]
90    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
91    pub fn count(&self) -> usize {
92        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
93        unsafe { ffi::mdl_asset_count(self.handle.as_ptr()) as usize }
94    }
95
96    #[must_use]
97    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
98    pub fn bounding_box(&self) -> BoundingBox {
99        let mut min = [0.0_f32; 3];
100        let mut max = [0.0_f32; 3];
101        // SAFETY: The unsafe operation is valid in this context.
102        unsafe {
103            ffi::mdl_asset_bounding_box(
104                self.handle.as_ptr(),
105                &mut min[0],
106                &mut min[1],
107                &mut min[2],
108                &mut max[0],
109                &mut max[1],
110                &mut max[2],
111            );
112        }
113        BoundingBox { min, max }
114    }
115
116    #[must_use]
117    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
118    pub fn bounding_box_at_time(&self, time: f64) -> BoundingBox {
119        let mut min = [0.0_f32; 3];
120        let mut max = [0.0_f32; 3];
121        // SAFETY: The unsafe operation is valid in this context.
122        unsafe {
123            ffi::mdl_asset_bounding_box_at_time(
124                self.handle.as_ptr(),
125                time,
126                &mut min[0],
127                &mut min[1],
128                &mut min[2],
129                &mut max[0],
130                &mut max[1],
131                &mut max[2],
132            );
133        }
134        BoundingBox { min, max }
135    }
136
137    #[must_use]
138    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
139    pub fn url(&self) -> Option<String> {
140        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
141        take_string(unsafe { ffi::mdl_asset_url_string(self.handle.as_ptr()) })
142    }
143
144    #[must_use]
145    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
146    pub fn frame_interval(&self) -> f64 {
147        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
148        unsafe { ffi::mdl_asset_frame_interval(self.handle.as_ptr()) }
149    }
150
151    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
152    pub fn set_frame_interval(&self, value: f64) {
153        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
154        unsafe { ffi::mdl_asset_set_frame_interval(self.handle.as_ptr(), value) };
155    }
156
157    #[must_use]
158    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
159    pub fn start_time(&self) -> f64 {
160        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
161        unsafe { ffi::mdl_asset_start_time(self.handle.as_ptr()) }
162    }
163
164    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
165    pub fn set_start_time(&self, value: f64) {
166        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
167        unsafe { ffi::mdl_asset_set_start_time(self.handle.as_ptr(), value) };
168    }
169
170    #[must_use]
171    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
172    pub fn end_time(&self) -> f64 {
173        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
174        unsafe { ffi::mdl_asset_end_time(self.handle.as_ptr()) }
175    }
176
177    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
178    pub fn set_end_time(&self, value: f64) {
179        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
180        unsafe { ffi::mdl_asset_set_end_time(self.handle.as_ptr(), value) };
181    }
182
183    #[must_use]
184    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
185    pub fn up_axis(&self) -> [f32; 3] {
186        let mut axis = [0.0_f32; 3];
187        // SAFETY: The unsafe operation is valid in this context.
188        unsafe {
189            ffi::mdl_asset_up_axis(
190                self.handle.as_ptr(),
191                &mut axis[0],
192                &mut axis[1],
193                &mut axis[2],
194            );
195        };
196        axis
197    }
198
199    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
200    pub fn set_up_axis(&self, axis: [f32; 3]) {
201        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
202        unsafe { ffi::mdl_asset_set_up_axis(self.handle.as_ptr(), axis[0], axis[1], axis[2]) };
203    }
204
205    #[must_use]
206    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
207    pub fn object_at(&self, index: usize) -> Option<Object> {
208        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
209        let ptr = unsafe { ffi::mdl_asset_object_at_index(self.handle.as_ptr(), index as u64) };
210        // SAFETY: The unsafe operation is valid in this context.
211        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Object::from_handle)
212    }
213
214    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
215    pub fn object_at_path(&self, path: &str) -> Result<Option<Object>> {
216        let path = c_string(path)?;
217        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
218        let ptr = unsafe { ffi::mdl_asset_object_at_path(self.handle.as_ptr(), path.as_ptr()) };
219        // SAFETY: The unsafe operation is valid in this context.
220        Ok(unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Object::from_handle))
221    }
222
223    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
224    pub fn add_object(&self, object: &Object) {
225        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
226        unsafe { ffi::mdl_asset_add_object(self.handle.as_ptr(), object.as_ptr()) };
227    }
228
229    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
230    pub fn remove_object(&self, object: &Object) {
231        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
232        unsafe { ffi::mdl_asset_remove_object(self.handle.as_ptr(), object.as_ptr()) };
233    }
234
235    #[must_use]
236    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
237    pub fn mesh_at(&self, index: usize) -> Option<Mesh> {
238        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
239        let ptr = unsafe { ffi::mdl_asset_mesh_at_index(self.handle.as_ptr(), index as u64) };
240        // SAFETY: The unsafe operation is valid in this context.
241        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
242    }
243
244    #[must_use]
245    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
246    pub fn meshes(&self) -> Vec<Mesh> {
247        (0..self.count())
248            .filter_map(|index| self.mesh_at(index))
249            .collect()
250    }
251
252    /// Calls the corresponding Model I/O method on the wrapped Model I/O asset counterpart.
253    pub fn load_textures(&self) {
254        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
255        unsafe { ffi::mdl_asset_load_textures(self.handle.as_ptr()) };
256    }
257}