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}